This is an automated email from the ASF dual-hosted git repository.

asf-gitbox-commits pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-jcs.git

commit 02a06385a9484dde2ab96a21173777f8e1ffaea3
Author: Thomas Vandahl <[email protected]>
AuthorDate: Wed Sep 16 18:11:02 2026 +0200

    Move sharding to DoubleLinkedList
---
 .../AbstractDoubleLinkedListMemoryCache.java       | 171 ++++--------
 .../jcs4/utils/struct/DoubleLinkedList.java        | 293 +++++++++++++--------
 .../jcs4/utils/struct/DoubleLinkedListNode.java    |  25 +-
 .../utils/struct/DoubleLinkedListDumpUnitTest.java |   4 +-
 .../utils/struct/DoubleLinkedListUnitTest.java     |  14 +-
 5 files changed, 279 insertions(+), 228 deletions(-)

diff --git 
a/commons-jcs4-core/src/main/java/org/apache/commons/jcs4/engine/memory/AbstractDoubleLinkedListMemoryCache.java
 
b/commons-jcs4-core/src/main/java/org/apache/commons/jcs4/engine/memory/AbstractDoubleLinkedListMemoryCache.java
index 1ee65576..e0589973 100644
--- 
a/commons-jcs4-core/src/main/java/org/apache/commons/jcs4/engine/memory/AbstractDoubleLinkedListMemoryCache.java
+++ 
b/commons-jcs4-core/src/main/java/org/apache/commons/jcs4/engine/memory/AbstractDoubleLinkedListMemoryCache.java
@@ -20,11 +20,9 @@ package org.apache.commons.jcs4.engine.memory;
  */
 
 import java.io.IOException;
-import java.util.Arrays;
 import java.util.Map;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
-import java.util.concurrent.atomic.AtomicInteger;
 
 import org.apache.commons.jcs4.engine.behavior.ICacheElement;
 import org.apache.commons.jcs4.engine.control.CompositeCache;
@@ -53,41 +51,7 @@ public abstract class AbstractDoubleLinkedListMemoryCache<K, 
V> extends Abstract
     }
 
     /** Thread-safe double linked list for lru */
-    private DoubleLinkedList<MemoryElementDescriptor<K, V>>[] lists;
-
-    /** Number of shards */
-    private int shards;
-
-    private static class AtomicCyclicCounter
-    {
-        private final int max;
-        private final AtomicInteger counter;
-
-        private AtomicCyclicCounter(int max)
-        {
-            this.max = max;
-            counter = new AtomicInteger();
-        }
-
-        private int incrementAndGet()
-        {
-            return counter.accumulateAndGet(1, (index, inc) -> (++index >= max 
? 0 : index));
-        }
-    }
-
-    /** shard to spool */
-    private AtomicCyclicCounter spoolShard;
-
-    /**
-     * Returns the current cache shard for the given key
-     *
-     * @param key the cache key
-     * @return The shard
-     */
-    protected int spreadShard(K key)
-    {
-        return Math.abs(key.hashCode() % shards);
-    }
+    private DoubleLinkedList<MemoryElementDescriptor<K, V>> list;
 
     /**
      * Adds a new node to the start of the link list.
@@ -97,8 +61,7 @@ public abstract class AbstractDoubleLinkedListMemoryCache<K, 
V> extends Abstract
      */
     protected void addFirst(final MemoryElementDescriptor<K, V> me)
     {
-        int shard = spreadShard(me.getCacheElement().key());
-        lists[shard].addFirst(me);
+        list.addFirst(me);
         if ( log.isTraceEnabled() )
         {
             verifyCache(me.getCacheElement().key());
@@ -113,8 +76,7 @@ public abstract class AbstractDoubleLinkedListMemoryCache<K, 
V> extends Abstract
      */
     protected void addLast(final MemoryElementDescriptor<K,V> me)
     {
-        int shard = spreadShard(me.getCacheElement().key());
-        lists[shard].addLast(me);
+        list.addLast(me);
         if ( log.isTraceEnabled() )
         {
             verifyCache(me.getCacheElement().key());
@@ -153,11 +115,7 @@ public abstract class 
AbstractDoubleLinkedListMemoryCache<K, V> extends Abstract
     public IStats getStatistics()
     {
         final IStats stats = super.getStatistics();
-        stats.addStatElement("Shards", Integer.valueOf(shards));
-        for (int i = 0; i < shards; i++)
-        {
-            stats.addStatElement("List Size " + i, 
Integer.valueOf(lists[i].size()));
-        }
+        stats.addStatElement("Shards", Integer.valueOf(list.getShards()));
 
         return stats;
     }
@@ -167,18 +125,12 @@ public abstract class 
AbstractDoubleLinkedListMemoryCache<K, V> extends Abstract
      *
      * @param hub
      */
-    @SuppressWarnings("unchecked")
     @Override
     public void initialize(final CompositeCache<K, V> hub)
     {
         super.initialize(hub);
-        this.shards = getCacheAttributes().Shards();
-        lists = new DoubleLinkedList[shards];
-        for (int i = 0; i < shards; i++)
-        {
-            lists[i] = new DoubleLinkedList<>();
-        }
-        this.spoolShard = new AtomicCyclicCounter(shards);
+        int shards = getCacheAttributes().Shards();
+        list = new DoubleLinkedList<>(shards);
         log.info("initialized MemoryCache for {0}", this::getCacheName);
     }
 
@@ -191,7 +143,10 @@ public abstract class 
AbstractDoubleLinkedListMemoryCache<K, V> extends Abstract
     @Override
     protected MemoryElementDescriptor<K, V> wrap(ICacheElement<K, V> ce)
     {
-        return new MemoryElementDescriptor<>(ce);
+        MemoryElementDescriptor<K, V> me = new MemoryElementDescriptor<>(ce);
+        me.setShard(list.spreadShard(ce.key()));
+
+        return me;
     }
 
     /**
@@ -202,8 +157,7 @@ public abstract class 
AbstractDoubleLinkedListMemoryCache<K, V> extends Abstract
     @Override
     protected void adjustGetElement(final MemoryElementDescriptor<K, V> me)
     {
-        int shard = spreadShard(me.getCacheElement().key());
-        adjustListForGet(lists[shard], me);
+        adjustListForGet(list, me);
     }
 
     /**
@@ -215,8 +169,7 @@ public abstract class 
AbstractDoubleLinkedListMemoryCache<K, V> extends Abstract
     @Override
     protected void adjustUpdateElement(MemoryElementDescriptor<K, V> newNode) 
throws IOException
     {
-        int shard = spreadShard(newNode.getCacheElement().key());
-        lists[shard].makeFirst(newNode);
+        list.makeFirst(newNode);
     }
 
     /**
@@ -225,7 +178,7 @@ public abstract class 
AbstractDoubleLinkedListMemoryCache<K, V> extends Abstract
     @Override
     protected void adjustRemoveAll()
     {
-        Arrays.stream(lists).forEach(DoubleLinkedList::removeAll);
+        list.removeAll();
     }
 
     /**
@@ -236,8 +189,7 @@ public abstract class 
AbstractDoubleLinkedListMemoryCache<K, V> extends Abstract
     @Override
     protected void adjustRemoveElement(final MemoryElementDescriptor<K, V> me)
     {
-        int shard = spreadShard(me.getCacheElement().key());
-        lists[shard].remove(me);
+        list.remove(me);
     }
 
     /**
@@ -307,8 +259,7 @@ public abstract class 
AbstractDoubleLinkedListMemoryCache<K, V> extends Abstract
     {
         ICacheElement<K, V> toSpool = null;
 
-        int shard = this.spoolShard.incrementAndGet();
-        final MemoryElementDescriptor<K, V> last = lists[shard].getLast();
+        final MemoryElementDescriptor<K, V> last = list.getLast();
         if (last != null)
         {
             toSpool = last.getCacheElement();
@@ -337,13 +288,10 @@ public abstract class 
AbstractDoubleLinkedListMemoryCache<K, V> extends Abstract
     private void dumpCacheEntries()
     {
         log.trace("dumpingCacheEntries");
-        for (int i = 0; i < shards; i++)
+        for (MemoryElementDescriptor<K, V> me : list)
         {
-            for (MemoryElementDescriptor<K, V> me : lists[i])
-            {
-                log.trace("dumpCacheEntries> shard={0}, key={1}, val={2}", i,
-                        me.getCacheElement().key(), 
me.getCacheElement().value());
-            }
+            log.trace("dumpCacheEntries> key={0}, val={1}",
+                    me.getCacheElement().key(), me.getCacheElement().value());
         }
     }
 
@@ -356,48 +304,42 @@ public abstract class 
AbstractDoubleLinkedListMemoryCache<K, V> extends Abstract
         boolean found = false;
         Map<K, MemoryElementDescriptor<K, V>> mapView = getMapView();
         log.trace("verifycache[{0}]: map contains {1} elements, linked list "
-                + "contains {2} elements", getCacheName(), getSize(),
-                Arrays.stream(lists)
-                    .mapToInt(DoubleLinkedList::size)
-                    .sum());
+                + "contains {2} elements", getCacheName(), getSize(), 
list.size());
         log.trace("verifycache: checking linked list by key ");
-        for (int i = 0; i < shards; i++)
+        for (MemoryElementDescriptor<K, V> li : list)
         {
-            for (MemoryElementDescriptor<K, V> li : lists[i])
+            final K key = li.getCacheElement().key();
+            if (!mapView.containsKey(key))
             {
-                final K key = li.getCacheElement().key();
-                if (!mapView.containsKey(key))
+                log.error("verifycache[{0}]: map does not contain key : {1}",
+                        getCacheName(), key);
+                log.error("key class={0}", key.getClass());
+                log.error("key hashCode={0}", key.hashCode());
+                log.error("key toString={0}", key.toString());
+                if (key instanceof GroupAttrName name)
                 {
-                    log.error("verifycache[{0}]: map does not contain key : 
{1}",
-                            getCacheName(), key);
-                    log.error("key class={0}", key.getClass());
-                    log.error("key hashCode={0}", key.hashCode());
-                    log.error("key toString={0}", key.toString());
-                    if (key instanceof GroupAttrName name)
-                    {
-                        log.error("GroupID hashCode={0}", 
name.groupId().hashCode());
-                        log.error("GroupID.class={0}", 
name.groupId().getClass());
-                        log.error("AttrName hashCode={0}", 
name.attrName().hashCode());
-                        log.error("AttrName.class={0}", 
name.attrName().getClass());
-                    }
-                    dumpMap();
-                }
-                else if (mapView.get(key) == null)
-                {
-                    log.error("verifycache[{0}]: linked list retrieval 
returned "
-                            + "null for key: {1}", getCacheName(), key);
+                    log.error("GroupID hashCode={0}", 
name.groupId().hashCode());
+                    log.error("GroupID.class={0}", name.groupId().getClass());
+                    log.error("AttrName hashCode={0}", 
name.attrName().hashCode());
+                    log.error("AttrName.class={0}", 
name.attrName().getClass());
                 }
+                dumpMap();
+            }
+            else if (mapView.get(key) == null)
+            {
+                log.error("verifycache[{0}]: linked list retrieval returned "
+                        + "null for key: {1}", getCacheName(), key);
             }
+        }
 
-            log.trace("verifycache: checking linked list by value ");
-            for (MemoryElementDescriptor<K, V> li : lists[i])
+        log.trace("verifycache: checking linked list by value ");
+        for (MemoryElementDescriptor<K, V> li : list)
+        {
+            if (!mapView.containsValue(li))
             {
-                if (!mapView.containsValue(li))
-                {
-                    log.error("verifycache[{0}]: map does not contain value: 
{1}",
-                            getCacheName(), li);
-                    dumpMap();
-                }
+                log.error("verifycache[{0}]: map does not contain value: {1}",
+                        getCacheName(), li);
+                dumpMap();
             }
         }
 
@@ -406,17 +348,15 @@ public abstract class 
AbstractDoubleLinkedListMemoryCache<K, V> extends Abstract
         {
             found = false;
 
-            for (int i = 0; i < shards; i++)
+            for (MemoryElementDescriptor<K, V> li : list)
             {
-                for (MemoryElementDescriptor<K, V> li : lists[i])
+                if (val.equals(li.getCacheElement().key()))
                 {
-                    if (val.equals(li.getCacheElement().key()))
-                    {
-                        found = true;
-                        break;
-                    }
+                    found = true;
+                    break;
                 }
             }
+
             if (!found)
             {
                 log.error("verifycache[{0}]: key not found in list : {1}",
@@ -445,20 +385,19 @@ public abstract class 
AbstractDoubleLinkedListMemoryCache<K, V> extends Abstract
         boolean found = false;
 
         // go through the linked list looking for the key
-        int shard = spreadShard(key);
-        for (MemoryElementDescriptor<K, V> li : lists[shard])
+        for (MemoryElementDescriptor<K, V> li : list)
         {
             if (li.getCacheElement().key() == key)
             {
                 found = true;
-                log.trace("verifycache(key) shard: {0}, key match: {1}", 
shard, key);
+                log.trace("verifycache(key) key match: {0}", key);
                 break;
             }
         }
         if (!found)
         {
-            log.error("verifycache(key)[{0}], shard {1}, couldn't find key! : 
{2}",
-                    getCacheName(), shard, key);
+            log.error("verifycache(key)[{0}], couldn't find key! : {1}",
+                    getCacheName(), key);
         }
     }
 }
diff --git 
a/commons-jcs4-core/src/main/java/org/apache/commons/jcs4/utils/struct/DoubleLinkedList.java
 
b/commons-jcs4-core/src/main/java/org/apache/commons/jcs4/utils/struct/DoubleLinkedList.java
index 950a524d..87e2c264 100644
--- 
a/commons-jcs4-core/src/main/java/org/apache/commons/jcs4/utils/struct/DoubleLinkedList.java
+++ 
b/commons-jcs4-core/src/main/java/org/apache/commons/jcs4/utils/struct/DoubleLinkedList.java
@@ -1,6 +1,7 @@
 package org.apache.commons.jcs4.utils.struct;
 
 import java.util.Iterator;
+import java.util.concurrent.atomic.AtomicInteger;
 import java.util.concurrent.locks.Lock;
 import java.util.concurrent.locks.ReentrantLock;
 
@@ -26,33 +27,12 @@ import java.util.concurrent.locks.ReentrantLock;
 import org.apache.commons.jcs4.log.Log;
 
 /**
- * This is a generic double linked list. Thread safety is NOT provided by this 
class.
- * <p>
- * <b>THREAD SAFETY REQUIREMENT:</b> This class must be guarded by external 
synchronization
- * in calling code. All operations assume the caller holds appropriate locks
- * (e.g., ReentrantLock in {@link 
org.apache.commons.jcs4.engine.memory.AbstractDoubleLinkedListMemoryCache}).
- * <p>
- * This design eliminates double-locking problems when used with external 
locks and provides
- * O(1) performance for node repositioning operations.
- * <p>
- * <b>Example Usage:</b>
- * <pre>
- * Lock lock = new java.util.concurrent.locks.ReentrantLock();
- * DoubleLinkedList&lt;MyNode&gt; list = new DoubleLinkedList&lt;&gt;();
- *
- * // Caller must acquire lock before accessing
- * lock.lock();
- * try {
- *     list.addFirst(node);  // SAFE because lock is held
- * } finally {
- *     lock.unlock();
- * }
- * </pre>
+ * This is a generic thread-safe double linked list. It uses internal locking 
and sharding to achieve
+ * high throughput and little lock contention
  *
  * @see java.util.concurrent.locks.ReentrantLock
  * @see 
org.apache.commons.jcs4.engine.memory.AbstractDoubleLinkedListMemoryCache
  */
-@SuppressWarnings({"unchecked"}) // Don't know how to resolve this with 
generics
 public class DoubleLinkedList<T extends DoubleLinkedListNode>
     implements Iterable<T>
 {
@@ -62,25 +42,83 @@ public class DoubleLinkedList<T extends 
DoubleLinkedListNode>
     /** Record size to avoid having to iterate */
     private int size;
 
-    /** The lock */
-    private final Lock lock;
+    /** Number of shards */
+    private final int shards;
+
+    /** The locks */
+    private final Lock[] lock;
 
     /** LRU double linked list head node */
-    private T first;
+    private DoubleLinkedListNode[] first;
 
     /** LRU double linked list tail node */
-    private T last;
+    private DoubleLinkedListNode[] last;
+
+    private static class AtomicCyclicCounter
+    {
+        private final int max;
+        private final AtomicInteger counter;
+
+        private AtomicCyclicCounter(int max)
+        {
+            this.max = max;
+            counter = new AtomicInteger();
+        }
+
+        private int incrementAndGet()
+        {
+            return counter.accumulateAndGet(1, (index, inc) -> (++index >= max 
? 0 : index));
+        }
+    }
+
+    /** shard to spool */
+    private final AtomicCyclicCounter spoolShard;
 
     /**
      * Construct DoubleLinkedList
+     *
+     * @param shards Number of shards
      */
-    public DoubleLinkedList()
+    public DoubleLinkedList(int shards)
     {
-        this.first = (T) new DoubleLinkedListNode();
-        this.last = (T) new DoubleLinkedListNode();
-        this.first.next = this.last;
-        this.last.prev = this.first;
-        this.lock = new ReentrantLock();
+        this.shards = shards;
+        this.lock = new Lock[shards];
+        this.first = new DoubleLinkedListNode[shards];
+        this.last = new DoubleLinkedListNode[shards];
+        this.spoolShard = new AtomicCyclicCounter(shards);
+
+        for (int i = 0; i < shards; i++)
+        {
+            first[i] = new DoubleLinkedListNode();
+            first[i].setShard(i);
+            last[i] = new DoubleLinkedListNode();
+            last[i].setShard(i);
+            first[i].next = this.last[i];
+            last[i].prev = this.first[i];
+            lock[i] = new ReentrantLock();
+        }
+    }
+
+    /**
+     * Returns the number of shards
+     *
+     * @return the shards
+     */
+    public int getShards()
+    {
+        return shards;
+    }
+
+
+    /**
+     * Returns the current cache shard for the given key
+     *
+     * @param key the cache key
+     * @return The shard
+     */
+    public int spreadShard(Object key)
+    {
+        return Math.abs(key.hashCode() % shards);
     }
 
     /**
@@ -90,18 +128,19 @@ public class DoubleLinkedList<T extends 
DoubleLinkedListNode>
      */
     public void addFirst(final T me)
     {
-        lock.lock();
+        int shard = me.getShard();
+        lock[shard].lock();
         try
         {
-            me.prev = first;
-            me.next = first.next;
-            first.next.prev = me;
-            first.next = me;
+            me.prev = first[shard];
+            me.next = first[shard].next;
+            first[shard].next.prev = me;
+            first[shard].next = me;
             size++;
         }
         finally
         {
-            lock.unlock();
+            lock[shard].unlock();
         }
     }
 
@@ -112,18 +151,19 @@ public class DoubleLinkedList<T extends 
DoubleLinkedListNode>
      */
     public void addLast(final T me)
     {
-        lock.lock();
+        int shard = me.getShard();
+        lock[shard].lock();
         try
         {
-            me.next = last;
-            me.prev = last.prev;
-            last.prev.next = me;
-            last.prev = me;
+            me.next = last[shard];
+            me.prev = last[shard].prev;
+            last[shard].prev.next = me;
+            last[shard].prev = me;
             size++;
         }
         finally
         {
-            lock.unlock();
+            lock[shard].unlock();
         }
     }
 
@@ -144,27 +184,47 @@ public class DoubleLinkedList<T extends 
DoubleLinkedListNode>
     }
 
     /**
-     * Removes the specified node from the link list.
+     * Returns the first node from the link list.
      *
-     * @return DoubleLinkedListNode, the first node.
+     * @return the first node, null if the list is empty.
      */
+    @SuppressWarnings({"unchecked"}) // Don't know how to resolve this with 
generics
     public T getFirst()
     {
         log.debug("returning first node");
-        DoubleLinkedListNode f = first.next;
-        return (T) (f == last ? null : f);
+        int shard = this.spoolShard.incrementAndGet();
+        lock[shard].lock();
+        try
+        {
+            DoubleLinkedListNode f = first[shard].next;
+            return (T) (f == last[shard] ? null : f);
+        }
+        finally
+        {
+            lock[shard].unlock();
+        }
     }
 
     /**
      * Returns the last node from the link list, if there are any nodes.
      *
-     * @return The last node.
+     * @return The last node, null if the list is empty.
      */
+    @SuppressWarnings({"unchecked"}) // Don't know how to resolve this with 
generics
     public T getLast()
     {
         log.debug("returning last node");
-        DoubleLinkedListNode l = last.prev;
-        return (T) (l == first ? null : l);
+        int shard = this.spoolShard.incrementAndGet();
+        lock[shard].lock();
+        try
+        {
+            DoubleLinkedListNode l = last[shard].prev;
+            return (T) (l == first[shard] ? null : l);
+        }
+        finally
+        {
+            lock[shard].unlock();
+        }
     }
 
     /**
@@ -174,7 +234,8 @@ public class DoubleLinkedList<T extends 
DoubleLinkedListNode>
      */
     public void makeFirst(final T ln)
     {
-        lock.lock();
+        int shard = ln.getShard();
+        lock[shard].lock();
         try
         {
             if (ln.prev != null && ln.next != null)
@@ -183,15 +244,15 @@ public class DoubleLinkedList<T extends 
DoubleLinkedListNode>
                 ln.next.prev = ln.prev;
                 size--;
             }
-            ln.prev = first;
-            ln.next = first.next;
-            first.next.prev = ln;
-            first.next = ln;
+            ln.prev = first[shard];
+            ln.next = first[shard].next;
+            first[shard].next.prev = ln;
+            first[shard].next = ln;
             size++;
         }
         finally
         {
-            lock.unlock();
+            lock[shard].unlock();
         }
     }
 
@@ -202,7 +263,8 @@ public class DoubleLinkedList<T extends 
DoubleLinkedListNode>
      */
     public void makeLast(final T ln)
     {
-        lock.lock();
+        int shard = ln.getShard();
+        lock[shard].lock();
         try
         {
             if (ln.prev != null && ln.next != null)
@@ -211,15 +273,15 @@ public class DoubleLinkedList<T extends 
DoubleLinkedListNode>
                 ln.next.prev = ln.prev;
                 size--;
             }
-            ln.next = last;
-            ln.prev = last.prev;
-            last.prev.next = ln;
-            last.prev = ln;
+            ln.next = last[shard];
+            ln.prev = last[shard].prev;
+            last[shard].prev.next = ln;
+            last[shard].prev = ln;
             size++;
         }
         finally
         {
-            lock.unlock();
+            lock[shard].unlock();
         }
     }
 
@@ -232,7 +294,8 @@ public class DoubleLinkedList<T extends 
DoubleLinkedListNode>
     public boolean remove(final T me)
     {
         log.debug("removing node");
-        lock.lock();
+        int shard = me.getShard();
+        lock[shard].lock();
         try
         {
             if (me.prev != null && me.next != null)
@@ -245,7 +308,7 @@ public class DoubleLinkedList<T extends 
DoubleLinkedListNode>
         }
         finally
         {
-            lock.unlock();
+            lock[shard].unlock();
         }
 
         return true;
@@ -256,25 +319,28 @@ public class DoubleLinkedList<T extends 
DoubleLinkedListNode>
      */
     public void removeAll()
     {
-        DoubleLinkedListNode me = getFirst();
-        lock.lock();
-        try
+        for (int i = 0; i < shards; i++)
         {
-            while (me != null && me.next != null)
+            lock[i].lock();
+            try
             {
-                DoubleLinkedListNode toRemove = me;
-                me = me.next;
-                toRemove.prev = null;
-                toRemove.next = null;
+                DoubleLinkedListNode me = first[i].next;
+                while (me != last[i] && me.next != null)
+                {
+                    DoubleLinkedListNode toRemove = me;
+                    me = me.next;
+                    toRemove.prev = null;
+                    toRemove.next = null;
+                }
+                first[i].next = last[i];
+                last[i].prev = first[i];
+            }
+            finally
+            {
+                lock[i].unlock();
             }
-            first.next = last;
-            last.prev = first;
-            size = 0;
-        }
-        finally
-        {
-            lock.unlock();
         }
+        size = 0;
     }
 
     /**
@@ -285,22 +351,13 @@ public class DoubleLinkedList<T extends 
DoubleLinkedListNode>
     public T removeLast()
     {
         log.debug("removing last node");
-        lock.lock();
-        try
-        {
-            final T temp = (T) last.prev;
-            if (temp != first)
-            {
-                remove(temp);
-                return temp;
-            }
-        }
-        finally
+        T me = getLast();
+        if (me != null)
         {
-            lock.unlock();
+            remove(me);
         }
 
-        return null;
+        return me;
     }
 
     /**
@@ -314,42 +371,72 @@ public class DoubleLinkedList<T extends 
DoubleLinkedListNode>
     }
 
     /**
-     * Return an iterator over this list
+     * Return an iterator over this list of lists
      *
      * @return the iterator
      */
     @Override
+    @SuppressWarnings({"unchecked"}) // Don't know how to resolve this with 
generics
     public Iterator<T> iterator()
     {
         return new Iterator<>()
         {
-            private T runner = first;
+            private int shard = 0;
+            private T runner = (T) first[shard];
 
             @Override
-            public boolean hasNext()
+            public synchronized boolean hasNext()
             {
-                lock.lock();
+                if (shard >= shards)
+                {
+                    return false;
+                }
+
+                boolean rollover = false;
+
+                lock[shard].lock();
                 try
                 {
-                    return runner.next != null && runner.next != last;
+                    if (runner.next == null)
+                    {
+                        return false;
+                    }
+                    rollover = runner.next == last[shard];
+                    if (!rollover)
+                    {
+                        return true;
+                    }
                 }
                 finally
                 {
-                    lock.unlock();
+                    lock[shard].unlock();
+                }
+
+                if (rollover)
+                {
+                    shard++;
+                    if (shard < shards)
+                    {
+                        runner = (T) first[shard];
+                    }
+
+                    return hasNext();
                 }
+
+                return false;
             }
 
             @Override
-            public T next()
+            public synchronized T next()
             {
-                lock.lock();
+                lock[shard].lock();
                 try
                 {
                     runner = (T) runner.next;
                 }
                 finally
                 {
-                    lock.unlock();
+                    lock[shard].unlock();
                 }
                 return runner;
             }
diff --git 
a/commons-jcs4-core/src/main/java/org/apache/commons/jcs4/utils/struct/DoubleLinkedListNode.java
 
b/commons-jcs4-core/src/main/java/org/apache/commons/jcs4/utils/struct/DoubleLinkedListNode.java
index 55612a72..f1f0d1b9 100644
--- 
a/commons-jcs4-core/src/main/java/org/apache/commons/jcs4/utils/struct/DoubleLinkedListNode.java
+++ 
b/commons-jcs4-core/src/main/java/org/apache/commons/jcs4/utils/struct/DoubleLinkedListNode.java
@@ -35,6 +35,29 @@ public class DoubleLinkedListNode
     /** Don't change. */
     private static final long serialVersionUID = -1114934407695836097L;
 
+    /** Number of the shard I belong to */
+    private volatile int shard = 0;
+
     /** Double Linked list references */
-    public volatile DoubleLinkedListNode prev, next;
+    protected volatile DoubleLinkedListNode prev, next;
+
+    /**
+     * Returns the shard of this node
+     *
+     * @return the shard
+     */
+    public int getShard()
+    {
+        return shard;
+    }
+
+    /**
+     * Sets the shard of this node
+     *
+     * @param shard the shard to set
+     */
+    public void setShard(int shard)
+    {
+        this.shard = shard;
+    }
 }
diff --git 
a/commons-jcs4-core/src/test/java/org/apache/commons/jcs4/utils/struct/DoubleLinkedListDumpUnitTest.java
 
b/commons-jcs4-core/src/test/java/org/apache/commons/jcs4/utils/struct/DoubleLinkedListDumpUnitTest.java
index d3f279e2..ee2e310e 100644
--- 
a/commons-jcs4-core/src/test/java/org/apache/commons/jcs4/utils/struct/DoubleLinkedListDumpUnitTest.java
+++ 
b/commons-jcs4-core/src/test/java/org/apache/commons/jcs4/utils/struct/DoubleLinkedListDumpUnitTest.java
@@ -37,10 +37,12 @@ class DoubleLinkedListDumpUnitTest
         final StringWriter stringWriter = new StringWriter();
         TestLogConfigurationUtil.configureLogger( stringWriter, 
DoubleLinkedList.class.getName() );
 
-        final DoubleLinkedList<DoubleLinkedListNode> list = new 
DoubleLinkedList<>();
+        final DoubleLinkedList<DoubleLinkedListNode> list = new 
DoubleLinkedList<>(2);
 
         final DoubleLinkedListNode node1 = new DoubleLinkedListNode();
+        node1.setShard(0);
         final DoubleLinkedListNode node2 = new DoubleLinkedListNode();
+        node2.setShard(1);
 
         list.addLast( node1 );
         list.addLast( node2 );
diff --git 
a/commons-jcs4-core/src/test/java/org/apache/commons/jcs4/utils/struct/DoubleLinkedListUnitTest.java
 
b/commons-jcs4-core/src/test/java/org/apache/commons/jcs4/utils/struct/DoubleLinkedListUnitTest.java
index dd1be24a..69ad5927 100644
--- 
a/commons-jcs4-core/src/test/java/org/apache/commons/jcs4/utils/struct/DoubleLinkedListUnitTest.java
+++ 
b/commons-jcs4-core/src/test/java/org/apache/commons/jcs4/utils/struct/DoubleLinkedListUnitTest.java
@@ -32,7 +32,7 @@ class DoubleLinkedListUnitTest
     void testAddLast_Empty()
     {
         // SETUP
-        final DoubleLinkedList<DoubleLinkedListNode> list = new 
DoubleLinkedList<>();
+        final DoubleLinkedList<DoubleLinkedListNode> list = new 
DoubleLinkedList<>(1);
 
         final DoubleLinkedListNode node1 = new DoubleLinkedListNode();
 
@@ -48,7 +48,7 @@ class DoubleLinkedListUnitTest
     void testAddLast_NotEmpty()
     {
         // SETUP
-        final DoubleLinkedList<DoubleLinkedListNode> list = new 
DoubleLinkedList<>();
+        final DoubleLinkedList<DoubleLinkedListNode> list = new 
DoubleLinkedList<>(1);
 
         final DoubleLinkedListNode node1 = new DoubleLinkedListNode();
         final DoubleLinkedListNode node2 = new DoubleLinkedListNode();
@@ -66,7 +66,7 @@ class DoubleLinkedListUnitTest
     void testMakeLast_wasAlone()
     {
         // SETUP
-        final DoubleLinkedList<DoubleLinkedListNode> list = new 
DoubleLinkedList<>();
+        final DoubleLinkedList<DoubleLinkedListNode> list = new 
DoubleLinkedList<>(1);
 
         final DoubleLinkedListNode node1 = new DoubleLinkedListNode();
 
@@ -86,7 +86,7 @@ class DoubleLinkedListUnitTest
     void testMakeLast_wasFirst()
     {
         // SETUP
-        final DoubleLinkedList<DoubleLinkedListNode> list = new 
DoubleLinkedList<>();
+        final DoubleLinkedList<DoubleLinkedListNode> list = new 
DoubleLinkedList<>(1);
 
         final DoubleLinkedListNode node1 = new DoubleLinkedListNode();
         final DoubleLinkedListNode node2 = new DoubleLinkedListNode();
@@ -108,7 +108,7 @@ class DoubleLinkedListUnitTest
     void testMakeLast_wasInMiddle()
     {
         // SETUP
-        final DoubleLinkedList<DoubleLinkedListNode> list = new 
DoubleLinkedList<>();
+        final DoubleLinkedList<DoubleLinkedListNode> list = new 
DoubleLinkedList<>(1);
 
         final DoubleLinkedListNode node1 = new DoubleLinkedListNode();
         final DoubleLinkedListNode node2 = new DoubleLinkedListNode();
@@ -132,7 +132,7 @@ class DoubleLinkedListUnitTest
     void testMakeLast_wasLast()
     {
         // SETUP
-        final DoubleLinkedList<DoubleLinkedListNode> list = new 
DoubleLinkedList<>();
+        final DoubleLinkedList<DoubleLinkedListNode> list = new 
DoubleLinkedList<>(1);
 
         final DoubleLinkedListNode node1 = new DoubleLinkedListNode();
         final DoubleLinkedListNode node2 = new DoubleLinkedListNode();
@@ -154,7 +154,7 @@ class DoubleLinkedListUnitTest
     void testRemove()
     {
         // SETUP
-        final DoubleLinkedList<DoubleLinkedListNode> list = new 
DoubleLinkedList<>();
+        final DoubleLinkedList<DoubleLinkedListNode> list = new 
DoubleLinkedList<>(1);
 
         final DoubleLinkedListNode node1 = new DoubleLinkedListNode();
         final DoubleLinkedListNode node2 = new DoubleLinkedListNode();

Reply via email to