From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: JellySquid Date: Fri, 31 Jul 2020 21:46:32 -0500 Subject: [PATCH] lithium HashedList Original code by JellySquid, licensed under GNU Lesser General Public License v3.0 you can find the original code on https://github.com/CaffeineMC/lithium-fabric/ (Yarn mappings) diff --git a/src/main/java/me/jellysquid/mods/lithium/common/util/collections/HashedList.java b/src/main/java/me/jellysquid/mods/lithium/common/util/collections/HashedList.java new file mode 100644 index 0000000000000000000000000000000000000000..2d79932dbd1fc386a94b8d6ea3526934c54c2aad --- /dev/null +++ b/src/main/java/me/jellysquid/mods/lithium/common/util/collections/HashedList.java @@ -0,0 +1,280 @@ +package me.jellysquid.mods.lithium.common.util.collections; + +import it.unimi.dsi.fastutil.objects.Reference2IntOpenHashMap; +import it.unimi.dsi.fastutil.objects.ReferenceArrayList; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.ListIterator; +import java.util.NoSuchElementException; + +/** + * Wraps a {@link List} with a hash table which provides O(1) lookups for {@link Collection#contains(Object)}. The type + * contained by this list must use reference-equality semantics. + */ +@SuppressWarnings("SuspiciousMethodCalls") +public class HashedList implements List { + private final ReferenceArrayList list; + private final Reference2IntOpenHashMap counter; + + public HashedList(List list) { + this.list = new ReferenceArrayList<>(); + this.list.addAll(list); + + this.counter = new Reference2IntOpenHashMap<>(); + this.counter.defaultReturnValue(0); + + for (T obj : this.list) { + this.counter.addTo(obj, 1); + } + } + + @Override + public int size() { + return this.list.size(); + } + + @Override + public boolean isEmpty() { + return this.list.isEmpty(); + } + + @Override + public boolean contains(Object o) { + return this.counter.containsKey(o); + } + + @Override + public Iterator iterator() { + return this.listIterator(); + } + + @Override + public Object[] toArray() { + return this.list.toArray(); + } + + @SuppressWarnings("SuspiciousToArrayCall") + @Override + public T1[] toArray(T1[] a) { + return this.list.toArray(a); + } + + @Override + public boolean add(T t) { + this.trackReferenceAdded(t); + + return this.list.add(t); + } + + @Override + public boolean remove(Object o) { + this.trackReferenceRemoved(o); + + return this.list.remove(o); + } + + @Override + public boolean containsAll(Collection c) { + for (Object obj : c) { + if (!this.counter.containsKey(obj)) { + return false; + } + } + + return true; + } + + @Override + public boolean addAll(Collection c) { + for (T obj : c) { + this.trackReferenceAdded(obj); + } + + return this.list.addAll(c); + } + + @Override + public boolean addAll(int index, Collection c) { + for (T obj : c) { + this.trackReferenceAdded(obj); + } + + return this.list.addAll(index, c); + } + + @Override + public boolean removeAll(Collection c) { + for (Object obj : c) { + this.trackReferenceRemoved(obj); + } + + return this.list.removeAll(c); + } + + @Override + public boolean retainAll(Collection c) { + return this.list.retainAll(c); + } + + @Override + public void clear() { + this.counter.clear(); + this.list.clear(); + } + + @Override + public T get(int index) { + return this.list.get(index); + } + + @Override + public T set(int index, T element) { + T prev = this.list.set(index, element); + + if (prev != element) { + if (prev != null) { + this.trackReferenceRemoved(prev); + } + + this.trackReferenceAdded(element); + } + + return prev; + } + + @Override + public void add(int index, T element) { + this.trackReferenceAdded(element); + + this.list.add(index, element); + } + + @Override + public T remove(int index) { + T prev = this.list.remove(index); + + if (prev != null) { + this.trackReferenceRemoved(prev); + } + + return prev; + } + + @Override + public int indexOf(Object o) { + return this.list.indexOf(o); + } + + @Override + public int lastIndexOf(Object o) { + return this.list.lastIndexOf(o); + } + + @Override + public ListIterator listIterator() { + return this.listIterator(0); + } + + @Override + public ListIterator listIterator(int index) { + return new ListIterator() { + private final ListIterator inner = HashedList.this.list.listIterator(index); + + @Override + public boolean hasNext() { + return this.inner.hasNext(); + } + + @Override + public T next() { + return this.inner.next(); + } + + @Override + public boolean hasPrevious() { + return this.inner.hasPrevious(); + } + + @Override + public T previous() { + return this.inner.previous(); + } + + @Override + public int nextIndex() { + return this.inner.nextIndex(); + } + + @Override + public int previousIndex() { + return this.inner.previousIndex(); + } + + @Override + public void remove() { + int last = this.previousIndex(); + + if (last == -1) { + throw new NoSuchElementException(); + } + + T prev = HashedList.this.get(last); + + if (prev != null) { + HashedList.this.trackReferenceRemoved(prev); + } + + this.inner.remove(); + } + + @Override + public void set(T t) { + int last = this.previousIndex(); + + if (last == -1) { + throw new NoSuchElementException(); + } + + T prev = HashedList.this.get(last); + + if (prev != t) { + if (prev != null) { + HashedList.this.trackReferenceRemoved(prev); + } + + HashedList.this.trackReferenceAdded(t); + } + + this.inner.remove(); + } + + @Override + public void add(T t) { + HashedList.this.trackReferenceAdded(t); + + this.inner.add(t); + } + }; + } + + @Override + public List subList(int fromIndex, int toIndex) { + return this.list.subList(fromIndex, toIndex); + } + + private void trackReferenceAdded(T t) { + this.counter.addTo(t, 1); + } + + @SuppressWarnings("unchecked") + private void trackReferenceRemoved(Object o) { + if (this.counter.addTo((T) o, -1) <= 1) { + this.counter.removeInt(o); + } + } + + public static HashedList wrapper(List list) { + return new HashedList<>(list); + } +}