GH-2076: Fix for Iterator.remove in GraphMem
diff --git a/jena-core/src/main/java/org/apache/jena/mem/HashCommon.java b/jena-core/src/main/java/org/apache/jena/mem/HashCommon.java
index 6983c23..b1a597b 100644
--- a/jena-core/src/main/java/org/apache/jena/mem/HashCommon.java
+++ b/jena-core/src/main/java/org/apache/jena/mem/HashCommon.java
@@ -34,40 +34,40 @@
     /**
         Jeremy suggests, from his experiments, that load factors more than
         0.6 leave the table too dense, and little advantage is gained below 0.4.
-        Although that was with a quadratic probe, I'm borrowing the same 
-        plausible range, and use 0.5 by default. 
+        Although that was with a quadratic probe, I'm borrowing the same
+        plausible range, and use 0.5 by default.
     */
     protected static final double loadFactor = 0.5;
-    
+
     /**
         The keys of whatever table it is we're implementing. Since we share code
         for triple sets and for node->bunch maps, it has to be an Object array; we
         take the casting hit.
      */
     protected Key [] keys;
-    
+
     /**
         The capacity (length) of the key array.
     */
     public int capacity;
-    
+
     /**
         The threshold number of elements above which we resize the table;
         equal to the capacity times the load factor.
     */
     protected int threshold;
-    
+
     /**
         The number of active elements in the table, maintained incrementally.
     */
     protected int size = 0;
-    
+
     /**
         A count of the number of changes applied to this Hash object, used for
         detecting concurrent modifications.
     */
     protected int changes;
-    
+
     /**
         Initialise this hashed thingy to have <code>initialCapacity</code> as its
         capacity and the corresponding threshold. All the key elements start out
@@ -78,7 +78,7 @@
         keys = newKeyArray( capacity = initialCapacity );
         threshold = (int) (capacity * loadFactor);
         }
-    
+
     /**
         Subclasses must implement to answer a new Key[size] array.
     */
@@ -96,40 +96,40 @@
         /**
              A NotifyEmpty instance that ignores the notification.
         */
-        public static NotifyEmpty ignore = new NotifyEmpty() 
+        public static NotifyEmpty ignore = new NotifyEmpty()
             { @Override
             public void emptied() { }};
-        
+
         /**
              Method to call to notify that the collection has become empty.
         */
-        public void emptied(); 
-        }   
+        public void emptied();
+        }
 
     /**
-        When removeFrom [or remove] removes a key, it calls this method to 
-        remove any associated values, passing in the index of the key's slot. 
+        When removeFrom [or remove] removes a key, it calls this method to
+        remove any associated values, passing in the index of the key's slot.
         Subclasses override if they have any associated values.
     */
     protected void removeAssociatedValues( int here )
         {}
 
     /**
-        When removeFrom [or remove] moves a key, it calls this method to move 
+        When removeFrom [or remove] moves a key, it calls this method to move
         any associated values, passing in the index of the slot <code>here</code>
         to move to and the index of the slot <code>scan</code> to move from.
         Subclasses override if they have any associated values.
     */
     protected void moveAssociatedValues( int here, int scan )
         {}
-    
+
     /**
         Answer the item at index <code>i</code> of <code>keys</code>. This
         method is for testing purposes <i>only</i>.
     */
     public Object getItemForTestingAt( int i )
         { return keys[i]; }
-    
+
     /**
         Answer the initial index for the object <code>key</code> in the table.
         With luck, this will be the final position for that object. The initial index
@@ -148,8 +148,8 @@
         voodoo to (try to) eliminate problems experienced by Wolfgang.
     */
     protected int improveHashCode( int hashCode )
-        { return hashCode * 127; }    
-    
+        { return hashCode * 127; }
+
     /**
         Search for the slot in which <code>key</code> is found. If it is absent,
         return the index of the free slot in which it could be placed. If it is present,
@@ -163,11 +163,11 @@
         while (true)
             {
             Key current = keys[index];
-            if (current == null) return index; 
+            if (current == null) return index;
             if (key.equals( current )) return ~index;
             if (--index < 0) index += capacity;
             }
-        }   
+        }
 
     /**
         Remove the object <code>key</code> from this hash's keys if it
@@ -184,7 +184,7 @@
         int slot = findSlot( key );
         if (slot < 0) removeFrom( ~slot );
         }
-    
+
     /**
         Work out the capacity and threshold sizes for a new improved bigger
         table (bigger by a factor of two, at present).
@@ -194,11 +194,11 @@
         capacity = nextSize( capacity * 2 );
         threshold = (int) (capacity * loadFactor);
         }
-     
+
     // Hash tables are 0.25 to 0.5 full so these numbers
-    // are for storing about 1/3 of that number of items. 
+    // are for storing about 1/3 of that number of items.
     // The larger sizes are added so that the system has "soft failure"
-    // rather implying guaranteed performance. 
+    // rather implying guaranteed performance.
     // https://primes.utm.edu/lists/small/millions/
     static final int [] primes =
         {
@@ -206,7 +206,7 @@
         19_853, 39_709, 79_423, 158_849, 317_701, 635_413,
         1_270_849, 2_541_701, 5_083_423
         , 10_166_857
-        , 20_333_759   
+        , 20_333_759
         , 40_667_527
         , 81_335_047
         , 162_670_111
@@ -214,17 +214,17 @@
         , 650_680_469
         , 982_451_653 // 50 millionth prime - Largest at primes.utm.edu.
         };
-    
+
     protected static int nextSize(int atLeast) {
         for ( int prime : primes ) {
             if ( prime > atLeast )
                 return prime;
         }
         //return atLeast ;        // Input is 2*current capacity.
-        // There are some very large numbers in the primes table. 
-        throw new JenaException("Failed to find a 'next size': atleast = "+atLeast) ; 
+        // There are some very large numbers in the primes table.
+        throw new JenaException("Failed to find a 'next size': atleast = "+atLeast) ;
     }
-    
+
     /**
         Remove the triple at element <code>i</code> of <code>contents</code>.
         This is an implementation of Knuth's Algorithm R from tAoCP vol3, p 527,
@@ -268,8 +268,8 @@
                     }
                 }
             }
-        }    
-    
+        }
+
     void showkeys()
         {
         if (false)
@@ -283,7 +283,7 @@
 
     public ExtendedIterator<Key> keyIterator()
         { return keyIterator( NotifyEmpty.ignore ); }
-    
+
     public ExtendedIterator<Key> keyIterator( final NotifyEmpty container )
         {
         showkeys();
@@ -292,7 +292,7 @@
         ExtendedIterator<Key> leftovers = new MovedKeysIterator( changes, container, movedKeys );
         return basic.andThen( leftovers );
         }
-    
+
     /**
         The MovedKeysIterator iterates over the elements of the <code>keys</code>
         list. It's not sufficient to just use List::iterator, because the .remove
@@ -310,14 +310,14 @@
         final NotifyEmpty container;
 
         protected MovedKeysIterator( int initialChanges, NotifyEmpty container, List<Key> keys )
-            { 
-            this.movedKeys = keys; 
-            this.initialChanges = initialChanges; 
+            {
+            this.movedKeys = keys;
+            this.initialChanges = initialChanges;
             this.container = container;
             }
 
         @Override public boolean hasNext()
-            { 
+            {
             return index < movedKeys.size();
             }
 
@@ -335,9 +335,9 @@
             }
 
         @Override public void remove()
-            { 
+            {
             if (changes > initialChanges) throw new ConcurrentModificationException();
-            primitiveRemove( movedKeys.get( index - 1 ) ); 
+            primitiveRemove( movedKeys.get( index - 1 ) );
             if (size == 0) container.emptied();
             }
         }
@@ -357,9 +357,9 @@
         final NotifyEmpty container;
 
         protected BasicKeyIterator( int initialChanges, NotifyEmpty container, List<Key> movedKeys )
-            { 
-            this.movedKeys = movedKeys; 
-            this.initialChanges = initialChanges;  
+            {
+            this.movedKeys = movedKeys;
+            this.initialChanges = initialChanges;
             this.container = container;
             }
 
@@ -394,10 +394,9 @@
         @Override public void remove()
             {
             if (changes > initialChanges) throw new ConcurrentModificationException();
-            // System.err.println( ">> keyIterator::remove, size := " + size +
-            // ", removing " + keys[index + 1] );
             Key moved = removeFrom( pos + 1 );
-            if (moved != null) movedKeys.add( moved );
+            if (moved != null && ! movedKeys.contains(moved) )
+                movedKeys.add( moved );
             if (size == 0) container.emptied();
             if (size < 0) throw new BrokenException( "BROKEN" );
             showkeys();
diff --git a/jena-core/src/main/java/org/apache/jena/util/iterator/NiceIterator.java b/jena-core/src/main/java/org/apache/jena/util/iterator/NiceIterator.java
index 1696791..4a6871a 100644
--- a/jena-core/src/main/java/org/apache/jena/util/iterator/NiceIterator.java
+++ b/jena-core/src/main/java/org/apache/jena/util/iterator/NiceIterator.java
@@ -153,7 +153,8 @@
 
             @Override public void remove()
                 {
-                if (null == removeFrom) throw new IllegalStateException("no calls to next() since last call to remove()");
+                if (null == removeFrom)
+                    throw new IllegalStateException("no calls to next() since last call to remove()");
                 removeFrom.remove();
                 removeFrom = null;
                 }