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