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