1. /*
  2. * Copyright 2001-2004 The Apache Software Foundation
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. package org.apache.commons.collections.list;
  17. import java.io.IOException;
  18. import java.io.ObjectInputStream;
  19. import java.io.ObjectOutputStream;
  20. import java.io.Serializable;
  21. import java.util.Collection;
  22. /**
  23. * A <code>List</code> implementation that stores a cache of internal Node objects
  24. * in an effort to reduce wasteful object creation.
  25. * <p>
  26. * A linked list creates one Node for each item of data added. This can result in
  27. * a lot of object creation and garbage collection. This implementation seeks to
  28. * avoid that by maintaining a store of cached nodes.
  29. * <p>
  30. * This implementation is suitable for long-lived lists where both add and remove
  31. * are used. Short-lived lists, or lists which only grow will have worse performance
  32. * using this class.
  33. * <p>
  34. * <b>Note that this implementation is not synchronized.</b>
  35. *
  36. * @since Commons Collections 3.0
  37. * @version $Revision: 1.7 $ $Date: 2004/04/20 23:46:50 $
  38. *
  39. * @author Jeff Varszegi
  40. * @author Rich Dougherty
  41. * @author Phil Steitz
  42. * @author Stephen Colebourne
  43. */
  44. public class NodeCachingLinkedList extends AbstractLinkedList implements Serializable {
  45. /** Serialization version */
  46. static final long serialVersionUID = 6897789178562232073L;
  47. /**
  48. * The default value for {@link #maximumCacheSize}.
  49. */
  50. protected static final int DEFAULT_MAXIMUM_CACHE_SIZE = 20;
  51. /**
  52. * The first cached node, or <code>null</code> if no nodes are cached.
  53. * Cached nodes are stored in a singly-linked list with
  54. * <code>next</code> pointing to the next element.
  55. */
  56. protected transient Node firstCachedNode;
  57. /**
  58. * The size of the cache.
  59. */
  60. protected transient int cacheSize;
  61. /**
  62. * The maximum size of the cache.
  63. */
  64. protected int maximumCacheSize;
  65. //-----------------------------------------------------------------------
  66. /**
  67. * Constructor that creates.
  68. */
  69. public NodeCachingLinkedList() {
  70. this(DEFAULT_MAXIMUM_CACHE_SIZE);
  71. }
  72. /**
  73. * Constructor that copies the specified collection
  74. *
  75. * @param coll the collection to copy
  76. */
  77. public NodeCachingLinkedList(Collection coll) {
  78. super(coll);
  79. this.maximumCacheSize = DEFAULT_MAXIMUM_CACHE_SIZE;
  80. }
  81. /**
  82. * Constructor that species the maximum cache size.
  83. *
  84. * @param maximumCacheSize the maximum cache size
  85. */
  86. public NodeCachingLinkedList(int maximumCacheSize) {
  87. super();
  88. this.maximumCacheSize = maximumCacheSize;
  89. init(); // must call init() as use super();
  90. }
  91. //-----------------------------------------------------------------------
  92. /**
  93. * Gets the maximum size of the cache.
  94. *
  95. * @return the maximum cache size
  96. */
  97. protected int getMaximumCacheSize() {
  98. return maximumCacheSize;
  99. }
  100. /**
  101. * Sets the maximum size of the cache.
  102. *
  103. * @param maximumCacheSize the new maximum cache size
  104. */
  105. protected void setMaximumCacheSize(int maximumCacheSize) {
  106. this.maximumCacheSize = maximumCacheSize;
  107. shrinkCacheToMaximumSize();
  108. }
  109. /**
  110. * Reduce the size of the cache to the maximum, if necessary.
  111. */
  112. protected void shrinkCacheToMaximumSize() {
  113. // Rich Dougherty: This could be more efficient.
  114. while (cacheSize > maximumCacheSize) {
  115. getNodeFromCache();
  116. }
  117. }
  118. /**
  119. * Gets a node from the cache. If a node is returned, then the value of
  120. * {@link #cacheSize} is decreased accordingly. The node that is returned
  121. * will have <code>null</code> values for next, previous and element.
  122. *
  123. * @return a node, or <code>null</code> if there are no nodes in the cache.
  124. */
  125. protected Node getNodeFromCache() {
  126. if (cacheSize == 0) {
  127. return null;
  128. }
  129. Node cachedNode = firstCachedNode;
  130. firstCachedNode = cachedNode.next;
  131. cachedNode.next = null; // This should be changed anyway, but defensively
  132. // set it to null.
  133. cacheSize--;
  134. return cachedNode;
  135. }
  136. /**
  137. * Checks whether the cache is full.
  138. *
  139. * @return true if the cache is full
  140. */
  141. protected boolean isCacheFull() {
  142. return cacheSize >= maximumCacheSize;
  143. }
  144. /**
  145. * Adds a node to the cache, if the cache isn't full.
  146. * The node's contents are cleared to so they can be garbage collected.
  147. *
  148. * @param node the node to add to the cache
  149. */
  150. protected void addNodeToCache(Node node) {
  151. if (isCacheFull()) {
  152. // don't cache the node.
  153. return;
  154. }
  155. // clear the node's contents and add it to the cache.
  156. Node nextCachedNode = firstCachedNode;
  157. node.previous = null;
  158. node.next = nextCachedNode;
  159. node.setValue(null);
  160. firstCachedNode = node;
  161. cacheSize++;
  162. }
  163. //-----------------------------------------------------------------------
  164. /**
  165. * Creates a new node, either by reusing one from the cache or creating
  166. * a new one.
  167. *
  168. * @param value value of the new node
  169. * @return the newly created node
  170. */
  171. protected Node createNode(Object value) {
  172. Node cachedNode = getNodeFromCache();
  173. if (cachedNode == null) {
  174. return super.createNode(value);
  175. } else {
  176. cachedNode.setValue(value);
  177. return cachedNode;
  178. }
  179. }
  180. /**
  181. * Removes the node from the list, storing it in the cache for reuse
  182. * if the cache is not yet full.
  183. *
  184. * @param node the node to remove
  185. */
  186. protected void removeNode(Node node) {
  187. super.removeNode(node);
  188. addNodeToCache(node);
  189. }
  190. /**
  191. * Removes all the nodes from the list, storing as many as required in the
  192. * cache for reuse.
  193. *
  194. */
  195. protected void removeAllNodes() {
  196. // Add the removed nodes to the cache, then remove the rest.
  197. // We can add them to the cache before removing them, since
  198. // {@link AbstractLinkedList.removeAllNodes()} removes the
  199. // nodes by removing references directly from {@link #header}.
  200. int numberOfNodesToCache = Math.min(size, maximumCacheSize - cacheSize);
  201. Node node = header.next;
  202. for (int currentIndex = 0; currentIndex < numberOfNodesToCache; currentIndex++) {
  203. Node oldNode = node;
  204. node = node.next;
  205. addNodeToCache(oldNode);
  206. }
  207. super.removeAllNodes();
  208. }
  209. //-----------------------------------------------------------------------
  210. /**
  211. * Serializes the data held in this object to the stream specified.
  212. */
  213. private void writeObject(ObjectOutputStream out) throws IOException {
  214. out.defaultWriteObject();
  215. doWriteObject(out);
  216. }
  217. /**
  218. * Deserializes the data held in this object to the stream specified.
  219. */
  220. private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
  221. in.defaultReadObject();
  222. doReadObject(in);
  223. }
  224. }