All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.hornetq.utils.LinkedListImpl Maven / Gradle / Ivy

/*
 * Copyright 2005-2014 Red Hat, Inc.
 * Red Hat licenses this file to you under the Apache License, version
 * 2.0 (the "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *    http://www.apache.org/licenses/LICENSE-2.0
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
 * implied.  See the License for the specific language governing
 * permissions and limitations under the License.
 */
package org.hornetq.utils;

import java.lang.reflect.Array;
import java.util.NoSuchElementException;


/**
 * A linked list implementation which allows multiple iterators to exist at the same time on the queue, and which see any
 * elements added or removed from the queue either directly or via iterators.
 *
 * This class is not thread safe.
 *
 * @author Tim Fox
 *
 *
 */
public class LinkedListImpl implements LinkedList
{
   private static final int INITIAL_ITERATOR_ARRAY_SIZE = 10;

   private final Node head = new Node(null);

   private Node tail = null;

   private int size;

   // We store in an array rather than a Map for the best performance
   private volatile Iterator[] iters;

   private int numIters;

   private int nextIndex;

   public LinkedListImpl()
   {
      iters = createIteratorArray(INITIAL_ITERATOR_ARRAY_SIZE);
   }

   public void addHead(E e)
   {
      Node node = new Node(e);

      node.next = head.next;

      node.prev = head;

      head.next = node;

      if (size == 0)
      {
         tail = node;
      }
      else
      {
         // Need to set the previous element on the former head
         node.next.prev = node;
      }

      size++;
   }

   public void addTail(E e)
   {
      if (size == 0)
      {
         addHead(e);
      }
      else
      {
         Node node = new Node(e);

         node.prev = tail;

         tail.next = node;

         tail = node;

         size++;
      }
   }

   public E poll()
   {
      Node ret = head.next;

      if (ret != null)
      {
         removeAfter(head);

         return ret.val;
      }
      else
      {
         return null;
      }
   }

   public void clear()
   {
      tail = head.next = null;

      size = 0;
   }

   public int size()
   {
      return size;
   }

   public LinkedListIterator iterator()
   {
      return new Iterator();
   }

   public String toString()
   {
      StringBuilder str = new StringBuilder("LinkedListImpl [ ");

      Node node = head;

      while (node != null)
      {
         str.append(node.toString());

         if (node.next != null)
         {
            str.append(", ");
         }

         node = node.next;
      }

      return str.toString();
   }

   public int numIters()
   {
      return numIters;
   }

   private Iterator[] createIteratorArray(int size)
   {
      return (Iterator[])Array.newInstance(Iterator.class, size);
   }

   private void removeAfter(Node node)
   {
      Node toRemove = node.next;

      node.next = toRemove.next;

      if (toRemove.next != null)
      {
         toRemove.next.prev = node;
      }

      if (toRemove == tail)
      {
         tail = node;
      }

      size--;

      if (toRemove.iterCount != 0)
      {
         LinkedListImpl.this.nudgeIterators(toRemove);
      }

      //Help GC - otherwise GC potentially has to traverse a very long list to see if elements are reachable, this can result in OOM
      //https://jira.jboss.org/browse/HORNETQ-469
      toRemove.next = toRemove.prev = null;
   }

   private synchronized void nudgeIterators(Node node)
   {
      for (int i = 0; i < numIters; i++)
      {
         Iterator iter = iters[i];
         if (iter != null)
         {
            iter.nudged(node);
         }
      }
   }

   private synchronized void addIter(Iterator iter)
   {
      if (numIters == iters.length)
      {
         resize(2 * numIters);
      }

      iters[nextIndex++] = iter;

      numIters++;
   }

   private synchronized void resize(int newSize)
   {
      Iterator[] newIters = createIteratorArray(newSize);

      System.arraycopy(iters, 0, newIters, 0, numIters);

      iters = newIters;
   }

   private synchronized void removeIter(Iterator iter)
   {
      for (int i = 0; i < numIters; i++)
      {
         if (iter == iters[i])
         {
            iters[i] = null;

            if (i != numIters - 1)
            {
               // Fill in the hole

               System.arraycopy(iters, i + 1, iters, i, numIters - i - 1);
            }

            numIters--;

            if (numIters >= INITIAL_ITERATOR_ARRAY_SIZE && numIters == iters.length / 2)
            {
               resize(numIters);
            }

            nextIndex--;

            return;
         }
      }

      throw new IllegalStateException("Cannot find iter to remove");
   }

   private static final class Node
   {
      Node next;

      Node prev;

      final E val;

      int iterCount;

      Node(E e)
      {
         val = e;
      }

      public String toString()
      {
         return "Node, value = " + val;
      }
   }

   private class Iterator implements LinkedListIterator
   {
      Node last;

      Node current = head.next;

      boolean repeat;

      Iterator()
      {
         if (current != null)
         {
            current.iterCount++;
         }

         addIter(this);
      }

      public void repeat()
      {
         repeat = true;
      }

      public boolean hasNext()
      {
         Node e = getNode();

         if (e != null && (e != last || repeat))
         {
            return true;
         }

         return canAdvance();
      }

      public E next()
      {
         Node e = getNode();

         if (repeat)
         {
            repeat = false;

            if (e != null)
            {
               return e.val;
            }
            else
            {
               if (canAdvance())
               {
                  advance();

                  e = getNode();

                  return e.val;
               }
               else
               {
                  throw new NoSuchElementException();
               }
            }
         }

         if (e == null || e == last)
         {
            if (canAdvance())
            {
               advance();

               e = getNode();
            }
            else
            {
               throw new NoSuchElementException();
            }
         }

         last = e;

         repeat = false;

         return e.val;
      }

      public void remove()
      {
         if (last == null)
         {
            throw new NoSuchElementException();
         }

         if (current == null)
         {
            throw new NoSuchElementException();
         }

         LinkedListImpl.this.removeAfter(current.prev);

         last = null;
      }

      public void close()
      {
         removeIter(this);
      }

      public void nudged(Node node)
      {
         if (current == node)
         {
            if (canAdvance())
            {
               advance();
            }
            else
            {
               if (current.prev != head)
               {
                  current.iterCount--;

                  current = current.prev;

                  current.iterCount++;
               }
               else
               {
                  current = null;
               }
            }
         }
      }

      private Node getNode()
      {
         if (current == null)
         {
            current = head.next;

            if (current != null)
            {
               current.iterCount++;
            }
         }

         if (current != null)
         {
            return current;
         }
         else
         {
            return null;
         }
      }

      private boolean canAdvance()
      {
         if (current == null)
         {
            current = head.next;

            if (current != null)
            {
               current.iterCount++;
            }
         }

         return current != null && current.next != null;
      }

      private void advance()
      {
         if (current == null || current.next == null)
         {
            throw new NoSuchElementException();
         }

         current.iterCount--;

         current = current.next;

         current.iterCount++;
      }

   }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy