From fa145ff7c57da010f047b6b69d5e8719885a1f1c Mon Sep 17 00:00:00 2001 From: zzz <zzz@i2pmail.org> Date: Fri, 17 Jun 2022 10:21:15 -0400 Subject: [PATCH] Util: ArraySet improvements Was never used; prep for actual usage Move from router to core Add Set constructors that are more efficient Add addUnique() method Allow size of set passed in constructors Prepare for extension javadocs --- .../java/src/net/i2p}/util/ArraySet.java | 82 ++++++++++++++++--- 1 file changed, 70 insertions(+), 12 deletions(-) rename {router/java/src/net/i2p/router => core/java/src/net/i2p}/util/ArraySet.java (79%) diff --git a/router/java/src/net/i2p/router/util/ArraySet.java b/core/java/src/net/i2p/util/ArraySet.java similarity index 79% rename from router/java/src/net/i2p/router/util/ArraySet.java rename to core/java/src/net/i2p/util/ArraySet.java index a471e0e5f3..3ddab5afd5 100644 --- a/router/java/src/net/i2p/router/util/ArraySet.java +++ b/core/java/src/net/i2p/util/ArraySet.java @@ -1,4 +1,4 @@ -package net.i2p.router.util; +package net.i2p.util; import java.io.Serializable; import java.util.AbstractSet; @@ -10,15 +10,19 @@ import java.util.Set; /** * A small, fast Set with a maximum size, backed by a fixed-size array. + * Much more space-efficient than HashSet. * Unsynchronized, not thread-safe. * Null elements are not permitted. - * Not appropriate for large Sets. * - * @since 0.9.25 + * Not appropriate for large Sets that are modified. + * add(), remove(), and contains() are O(n). + * Warning: addAll() and the Collection constructor are O(n**2). + * + * @since 0.9.25, moved to net.i2p.util in 0.9.55 */ public class ArraySet<E> extends AbstractSet<E> implements Set<E> { public static final int MAX_CAPACITY = 32; - private final Object[] _entries; + protected final Object[] _entries; private final boolean _throwOnFull; private int _size; private int _overflowIndex; @@ -33,12 +37,51 @@ public class ArraySet<E> extends AbstractSet<E> implements Set<E> { } /** - * A fixed capacity of MAX_CAPACITY. + * A fixed capacity of max(MAX_CAPACITY, c.size()) + * Adds over capacity will throw a SetFullException. + * + * @since 0.9.55 + */ + public ArraySet(Set<? extends E> c) { + this(c, MAX_CAPACITY); + } + + /** + * A fixed capacity of max(capacity, c.size()) * Adds over capacity will throw a SetFullException. - * @throws SetFullException if more than MAX_CAPACITY unique elements in c. + * + * @since 0.9.55 + */ + public ArraySet(Set<? extends E> c, int capacity) { + this(Math.max(capacity, c.size())); + // we avoid the O(n**2) behavior of addAll() + for (E e : c) { + _entries[_size++] = e; + } + } + + /** + * A fixed capacity of max(MAX_CAPACITY, c.size()), which may be more than + * the resulting set size if there are duplicates in c. + * Adds over capacity will throw a SetFullException. + * + * Warning: O(n**2). */ public ArraySet(Collection<? extends E> c) { - this(); + this(c, MAX_CAPACITY); + } + + /** + * A fixed capacity of max(capacity, c.size()), which may be more than + * the resulting set size if there are duplicates in c. + * Adds over capacity will throw a SetFullException. + * + * Warning: O(n**2). + * + * @since 0.9.55 + */ + public ArraySet(Collection<? extends E> c, int capacity) { + this(Math.max(capacity, c.size())); addAll(c); } @@ -46,7 +89,7 @@ public class ArraySet<E> extends AbstractSet<E> implements Set<E> { * Adds over capacity will throw a SetFullException. * * @param capacity the maximum size - * @throws IllegalArgumentException if capacity less than 1 or more than MAX_CAPACITY. + * @throws IllegalArgumentException if capacity less than 1. */ public ArraySet(int capacity) { this(capacity, true); @@ -61,10 +104,10 @@ public class ArraySet<E> extends AbstractSet<E> implements Set<E> { * If throwOnFull is true, adds over capacity will throw a SetFullException. * * @param capacity the maximum size - * @throws IllegalArgumentException if capacity less than 1 or more than MAX_CAPACITY. + * @throws IllegalArgumentException if capacity less than 1. */ public ArraySet(int capacity, boolean throwOnFull) { - if (capacity <= 0 || capacity > MAX_CAPACITY) + if (capacity <= 0) throw new IllegalArgumentException("bad capacity"); _entries = new Object[capacity]; _throwOnFull = throwOnFull; @@ -73,7 +116,7 @@ public class ArraySet<E> extends AbstractSet<E> implements Set<E> { /** * @return -1 if not found or if o is null */ - private int indexOf(Object o) { + protected int indexOf(Object o) { if (o != null) { for (int i = 0; i < _size; i++) { if (o.equals(_entries[i])) @@ -96,6 +139,22 @@ public class ArraySet<E> extends AbstractSet<E> implements Set<E> { _entries[i] = o; return false; } + addUnique(o); + return true; + } + + /** + * Unconditionally add o to the set. + * This avoids the O(n) time of add(), but it's the caller's + * responsibility to ensure that o is not a duplicate. + * Any duplicate added will appear in the iterator. + * + * @param o non-null, NPE will not be thrown + * @throws SetFullException if throwOnFull was true in constructor + * @since 0.9.55 + */ + public void addUnique(E o) { + int i; if (_size >= _entries.length) { if (_throwOnFull) throw new SetFullException(); @@ -109,7 +168,6 @@ public class ArraySet<E> extends AbstractSet<E> implements Set<E> { i = _size++; } _entries[i] = o; - return true; } @Override -- GitLab