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 * <b>Note that this implementation is not synchronized.</b> 40 * </p> 41 * 42 * @since 3.0 43 * @deprecated parent {@link AbstractLinkedList} is source incompatible with List methods added in Java 21 44 */ 45 @Deprecated 46 public class NodeCachingLinkedList<E> extends AbstractLinkedList<E> implements Serializable { 47 48 /** Serialization version */ 49 private static final long serialVersionUID = 6897789178562232073L; 50 51 /** 52 * The default value for {@link #maximumCacheSize}. 53 */ 54 private static final int DEFAULT_MAXIMUM_CACHE_SIZE = 20; 55 56 /** 57 * The first cached node, or {@code null} if no nodes are cached. 58 * Cached nodes are stored in a singly-linked list with 59 * {@code next} pointing to the next element. 60 */ 61 private transient Node<E> firstCachedNode; 62 63 /** 64 * The size of the cache. 65 */ 66 private transient int cacheSize; 67 68 /** 69 * The maximum size of the cache. 70 */ 71 private int maximumCacheSize; 72 73 /** 74 * Constructor that creates. 75 */ 76 public NodeCachingLinkedList() { 77 this(DEFAULT_MAXIMUM_CACHE_SIZE); 78 } 79 80 /** 81 * Constructor that copies the specified collection 82 * 83 * @param coll the collection to copy 84 */ 85 public NodeCachingLinkedList(final Collection<? extends E> coll) { 86 super(coll); 87 this.maximumCacheSize = DEFAULT_MAXIMUM_CACHE_SIZE; 88 } 89 90 /** 91 * Constructor that species the maximum cache size. 92 * 93 * @param maximumCacheSize the maximum cache size 94 */ 95 public NodeCachingLinkedList(final int maximumCacheSize) { 96 this.maximumCacheSize = maximumCacheSize; 97 init(); // must call init() as use super(); 98 } 99 100 /** 101 * Adds a node to the cache, if the cache isn't full. 102 * The node's contents are cleared, so they can be garbage collected. 103 * 104 * @param node the node to add to the cache 105 */ 106 protected void addNodeToCache(final Node<E> node) { 107 if (isCacheFull()) { 108 // don't cache the node. 109 return; 110 } 111 // clear the node's contents and add it to the cache. 112 final Node<E> nextCachedNode = firstCachedNode; 113 node.previous = null; 114 node.next = nextCachedNode; 115 node.setValue(null); 116 firstCachedNode = node; 117 cacheSize++; 118 } 119 120 /** 121 * Creates a new node, either by reusing one from the cache or creating 122 * a new one. 123 * 124 * @param value value of the new node 125 * @return the newly created node 126 */ 127 @Override 128 protected Node<E> createNode(final E value) { 129 final Node<E> cachedNode = getNodeFromCache(); 130 if (cachedNode == null) { 131 return super.createNode(value); 132 } 133 cachedNode.setValue(value); 134 return cachedNode; 135 } 136 137 /** 138 * Gets the maximum size of the cache. 139 * 140 * @return the maximum cache size 141 */ 142 protected int getMaximumCacheSize() { 143 return maximumCacheSize; 144 } 145 146 /** 147 * Gets a node from the cache. If a node is returned, then the value of 148 * {@link #cacheSize} is decreased accordingly. The node that is returned 149 * will have {@code null} values for next, previous and element. 150 * 151 * @return a node, or {@code null} if there are no nodes in the cache. 152 */ 153 protected Node<E> getNodeFromCache() { 154 if (cacheSize == 0) { 155 return null; 156 } 157 final Node<E> cachedNode = firstCachedNode; 158 firstCachedNode = cachedNode.next; 159 cachedNode.next = null; // This should be changed anyway, but defensively 160 // set it to null. 161 cacheSize--; 162 return cachedNode; 163 } 164 165 /** 166 * Checks whether the cache is full. 167 * 168 * @return true if the cache is full 169 */ 170 protected boolean isCacheFull() { 171 return cacheSize >= maximumCacheSize; 172 } 173 174 /** 175 * Deserializes the data held in this object to the stream specified. 176 * 177 * @param in the input stream 178 * @throws IOException if an error occurs while reading from the stream 179 * @throws ClassNotFoundException if an object read from the stream can not be loaded 180 */ 181 private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException { 182 in.defaultReadObject(); 183 doReadObject(in); 184 } 185 186 /** 187 * Removes all the nodes from the list, storing as many as required in the 188 * cache for reuse. 189 */ 190 @Override 191 protected void removeAllNodes() { 192 // Add the removed nodes to the cache, then remove the rest. 193 // We can add them to the cache before removing them, since 194 // {@link AbstractLinkedList.removeAllNodes()} removes the 195 // nodes by removing references directly from {@link #header}. 196 final int numberOfNodesToCache = Math.min(size, maximumCacheSize - cacheSize); 197 Node<E> node = header.next; 198 for (int currentIndex = 0; currentIndex < numberOfNodesToCache; currentIndex++) { 199 final Node<E> oldNode = node; 200 node = node.next; 201 addNodeToCache(oldNode); 202 } 203 super.removeAllNodes(); 204 } 205 206 /** 207 * Removes the node from the list, storing it in the cache for reuse 208 * if the cache is not yet full. 209 * 210 * @param node the node to remove 211 */ 212 @Override 213 protected void removeNode(final Node<E> node) { 214 super.removeNode(node); 215 addNodeToCache(node); 216 } 217 218 /** 219 * Sets the maximum size of the cache. 220 * 221 * @param maximumCacheSize the new maximum cache size 222 */ 223 protected void setMaximumCacheSize(final int maximumCacheSize) { 224 this.maximumCacheSize = maximumCacheSize; 225 shrinkCacheToMaximumSize(); 226 } 227 228 /** 229 * Reduce the size of the cache to the maximum, if necessary. 230 */ 231 protected void shrinkCacheToMaximumSize() { 232 // Rich Dougherty: This could be more efficient. 233 while (cacheSize > maximumCacheSize) { 234 getNodeFromCache(); 235 } 236 } 237 238 /** 239 * Serializes the data held in this object to the stream specified. 240 * 241 * @param out the output stream 242 * @throws IOException if an error occurs while writing to the stream 243 */ 244 private void writeObject(final ObjectOutputStream out) throws IOException { 245 out.defaultWriteObject(); 246 doWriteObject(out); 247 } 248 249 }