001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * https://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.commons.collections4.list; 018 019import java.io.IOException; 020import java.io.ObjectInputStream; 021import java.io.ObjectOutputStream; 022import java.io.Serializable; 023import java.lang.ref.WeakReference; 024import java.util.ArrayList; 025import java.util.Collection; 026import java.util.ConcurrentModificationException; 027import java.util.Iterator; 028import java.util.LinkedList; 029import java.util.List; 030import java.util.ListIterator; 031 032/** 033 * A {@code List} implementation with a {@code ListIterator} that 034 * allows concurrent modifications to the underlying list. 035 * <p> 036 * This implementation supports all of the optional {@link List} operations. 037 * It extends {@code AbstractLinkedList} and thus provides the 038 * stack/queue/dequeue operations available in {@link LinkedList}. 039 * </p> 040 * <p> 041 * The main feature of this class is the ability to modify the list and the 042 * iterator at the same time. Both the {@link #listIterator()} and {@link #cursor()} 043 * methods provides access to a {@code Cursor} instance which extends 044 * {@code ListIterator}. The cursor allows changes to the list concurrent 045 * with changes to the iterator. Note that the {@link #iterator()} method and 046 * sublists do <strong>not</strong> provide this cursor behavior. 047 * </p> 048 * <p> 049 * The {@code Cursor} class is provided partly for backwards compatibility 050 * and partly because it allows the cursor to be directly closed. Closing the 051 * cursor is optional because references are held via a {@code WeakReference}. 052 * For most purposes, simply modify the iterator and list at will, and then let 053 * the garbage collector to the rest. 054 * </p> 055 * <p> 056 * <strong>Note that this implementation is not synchronized.</strong> 057 * </p> 058 * 059 * @param <E> The type of the elements in the list. 060 * @see java.util.LinkedList 061 * @since 1.0 062 * @deprecated parent {@link AbstractLinkedList} is source incompatible with List methods added in Java 21 063 */ 064@Deprecated 065public class CursorableLinkedList<E> extends AbstractLinkedList<E> implements Serializable { 066 067 /** 068 * An extended {@code ListIterator} that allows concurrent changes to 069 * the underlying list. 070 * 071 * @param <E> The type of elements in this cursor. 072 */ 073 public static class Cursor<E> extends AbstractLinkedList.LinkedListIterator<E> { 074 075 /** Is the cursor valid (not closed) */ 076 boolean valid = true; 077 078 /** Is the next index valid */ 079 boolean nextIndexValid = true; 080 081 /** Flag to indicate if the current element was removed by another object. */ 082 boolean currentRemovedByAnother; 083 084 /** 085 * Constructs a new cursor. 086 * 087 * @param parent The parent list 088 * @param index The index to start from 089 */ 090 protected Cursor(final CursorableLinkedList<E> parent, final int index) { 091 super(parent, index); 092 valid = true; 093 } 094 095 /** 096 * Adds an object to the list. 097 * The object added here will be the new 'previous' in the iterator. 098 * 099 * @param obj The object to add 100 */ 101 @Override 102 public void add(final E obj) { 103 // overridden, as the nodeInserted() method updates the iterator state 104 super.add(obj); 105 // matches the (next.previous == node) clause in nodeInserted() 106 // thus next gets changed - reset it again here 107 next = next.next; 108 } 109 110 /** 111 * Override superclass modCount check, and replace it with our valid flag. 112 */ 113 @Override 114 protected void checkModCount() { 115 if (!valid) { 116 throw new ConcurrentModificationException("Cursor closed"); 117 } 118 } 119 120 // set is not overridden, as it works ok 121 // note that we want it to throw an exception if the element being 122 // set has been removed from the real list (compare this with the 123 // remove method where we silently ignore this case) 124 125 /** 126 * Mark this cursor as no longer being needed. Any resources 127 * associated with this cursor are immediately released. 128 * In previous versions of this class, it was mandatory to close 129 * all cursor objects to avoid memory leaks. It is <em>no longer</em> 130 * necessary to call this close method; an instance of this class 131 * can now be treated exactly like a normal iterator. 132 */ 133 public void close() { 134 if (valid) { 135 ((CursorableLinkedList<E>) parent).unregisterCursor(this); 136 valid = false; 137 } 138 } 139 140 /** 141 * Gets the index of the next element to be returned. 142 * 143 * @return The next index 144 */ 145 @Override 146 public int nextIndex() { 147 if (!nextIndexValid) { 148 if (next == parent.header) { 149 nextIndex = parent.size(); 150 } else { 151 int pos = 0; 152 Node<E> temp = parent.header.next; 153 while (temp != next) { 154 pos++; 155 temp = temp.next; 156 } 157 nextIndex = pos; 158 } 159 nextIndexValid = true; 160 } 161 return nextIndex; 162 } 163 164 /** 165 * Handle event from the list when a node has changed. 166 * 167 * @param node The node that changed 168 */ 169 protected void nodeChanged(final Node<E> node) { 170 // do nothing 171 } 172 173 /** 174 * Handle event from the list when a node has been added. 175 * 176 * @param node The node that was added 177 */ 178 protected void nodeInserted(final Node<E> node) { 179 if (node.previous == current || next.previous == node) { 180 next = node; 181 } else { 182 nextIndexValid = false; 183 } 184 } 185 186 /** 187 * Handle event from the list when a node has been removed. 188 * 189 * @param node The node that was removed 190 */ 191 protected void nodeRemoved(final Node<E> node) { 192 if (node == next && node == current) { 193 // state where next() followed by previous() 194 next = node.next; 195 current = null; 196 currentRemovedByAnother = true; 197 } else if (node == next) { 198 // state where next() not followed by previous() 199 // and we are matching next node 200 next = node.next; 201 currentRemovedByAnother = false; 202 } else if (node == current) { 203 // state where next() not followed by previous() 204 // and we are matching current (last returned) node 205 current = null; 206 currentRemovedByAnother = true; 207 nextIndex--; 208 } else { 209 nextIndexValid = false; 210 currentRemovedByAnother = false; 211 } 212 } 213 214 /** 215 * Removes the item last returned by this iterator. 216 * <p> 217 * There may have been subsequent alterations to the list 218 * since you obtained this item, however you can still remove it. 219 * You can even remove it if the item is no longer in the main list. 220 * However, you can't call this method on the same iterator more 221 * than once without calling next() or previous(). 222 * 223 * @throws IllegalStateException if there is no item to remove 224 */ 225 @Override 226 public void remove() { 227 // overridden, as the nodeRemoved() method updates the iterator 228 // state in the parent.removeNode() call below 229 if (current == null && currentRemovedByAnother) { // NOPMD 230 // quietly ignore, as the last returned node was removed 231 // by the list or some other iterator 232 // by ignoring it, we keep this iterator independent of 233 // other changes as much as possible 234 } else { 235 checkModCount(); 236 parent.removeNode(getLastNodeReturned()); 237 } 238 currentRemovedByAnother = false; 239 } 240 } 241 242 /** 243 * A cursor for the sublist based on LinkedSubListIterator. 244 * 245 * @param <E> The type of elements in this cursor. 246 * @since 3.2 247 */ 248 protected static class SubCursor<E> extends Cursor<E> { 249 250 /** The parent list */ 251 protected final LinkedSubList<E> sub; 252 253 /** 254 * Constructs a new cursor. 255 * 256 * @param sub The sub list 257 * @param index The index to start from 258 */ 259 protected SubCursor(final LinkedSubList<E> sub, final int index) { 260 super((CursorableLinkedList<E>) sub.parent, index + sub.offset); 261 this.sub = sub; 262 } 263 264 @Override 265 public void add(final E obj) { 266 super.add(obj); 267 sub.expectedModCount = parent.modCount; 268 sub.size++; 269 } 270 271 @Override 272 public boolean hasNext() { 273 return nextIndex() < sub.size; 274 } 275 276 @Override 277 public boolean hasPrevious() { 278 return previousIndex() >= 0; 279 } 280 281 @Override 282 public int nextIndex() { 283 return super.nextIndex() - sub.offset; 284 } 285 286 @Override 287 public void remove() { 288 super.remove(); 289 sub.expectedModCount = parent.modCount; 290 sub.size--; 291 } 292 } 293 294 /** Ensure serialization compatibility */ 295 private static final long serialVersionUID = 8836393098519411393L; 296 297 /** A list of the cursor currently open on this list */ 298 private transient List<WeakReference<Cursor<E>>> cursors; 299 300 /** 301 * Constructor that creates. 302 */ 303 public CursorableLinkedList() { 304 init(); // must call init() as use super(); 305 } 306 307 /** 308 * Constructor that copies the specified collection 309 * 310 * @param coll The collection to copy 311 */ 312 public CursorableLinkedList(final Collection<? extends E> coll) { 313 super(coll); 314 } 315 316 /** 317 * Inserts a new node into the list. 318 * 319 * @param nodeToInsert new node to insert 320 * @param insertBeforeNode node to insert before 321 * @throws NullPointerException if either node is null 322 */ 323 @Override 324 protected void addNode(final Node<E> nodeToInsert, final Node<E> insertBeforeNode) { 325 super.addNode(nodeToInsert, insertBeforeNode); 326 broadcastNodeInserted(nodeToInsert); 327 } 328 329 /** 330 * Informs all of my registered cursors that the specified 331 * element was changed. 332 * 333 * @param node The node that was changed 334 */ 335 protected void broadcastNodeChanged(final Node<E> node) { 336 final Iterator<WeakReference<Cursor<E>>> it = cursors.iterator(); 337 while (it.hasNext()) { 338 final WeakReference<Cursor<E>> ref = it.next(); 339 final Cursor<E> cursor = ref.get(); 340 if (cursor == null) { 341 it.remove(); // clean up list 342 } else { 343 cursor.nodeChanged(node); 344 } 345 } 346 } 347 348 /** 349 * Informs all of my registered cursors that the specified 350 * element was just added to my list. 351 * 352 * @param node The node that was changed 353 */ 354 protected void broadcastNodeInserted(final Node<E> node) { 355 final Iterator<WeakReference<Cursor<E>>> it = cursors.iterator(); 356 while (it.hasNext()) { 357 final WeakReference<Cursor<E>> ref = it.next(); 358 final Cursor<E> cursor = ref.get(); 359 if (cursor == null) { 360 it.remove(); // clean up list 361 } else { 362 cursor.nodeInserted(node); 363 } 364 } 365 } 366 367 /** 368 * Informs all of my registered cursors that the specified 369 * element was just removed from my list. 370 * 371 * @param node The node that was changed 372 */ 373 protected void broadcastNodeRemoved(final Node<E> node) { 374 final Iterator<WeakReference<Cursor<E>>> it = cursors.iterator(); 375 while (it.hasNext()) { 376 final WeakReference<Cursor<E>> ref = it.next(); 377 final Cursor<E> cursor = ref.get(); 378 if (cursor == null) { 379 it.remove(); // clean up list 380 } else { 381 cursor.nodeRemoved(node); 382 } 383 } 384 } 385 386 /** 387 * Creates a list iterator for the sublist. 388 * 389 * @param subList The sublist to get an iterator for 390 * @param fromIndex The index to start from, relative to the sublist 391 * @return The list iterator for the sublist 392 */ 393 @Override 394 protected ListIterator<E> createSubListListIterator(final LinkedSubList<E> subList, final int fromIndex) { 395 final SubCursor<E> cursor = new SubCursor<>(subList, fromIndex); 396 registerCursor(cursor); 397 return cursor; 398 } 399 400 /** 401 * Returns a {@link Cursor} for iterating through the elements of this list. 402 * <p> 403 * A {@code Cursor} is a {@code ListIterator} with an additional 404 * {@code close()} method. Calling this method immediately discards the 405 * references to the cursor. If it is not called, then the garbage collector 406 * will still remove the reference as it is held via a {@code WeakReference}. 407 * <p> 408 * The cursor enables iteration and list changes to occur in any order without 409 * invalidating the iterator (from one thread). When elements are added to the 410 * list, an event is fired to all active cursors enabling them to adjust to the 411 * change in the list. 412 * <p> 413 * When the "current" (i.e., last returned by {@link ListIterator#next} 414 * or {@link ListIterator#previous}) element of the list is removed, 415 * the cursor automatically adjusts to the change (invalidating the 416 * last returned value such that it cannot be removed). 417 * <p> 418 * The {@link #listIterator()} method returns the same as this method, and can 419 * be cast to a {@code Cursor} if the {@code close} method is required. 420 * 421 * @return A new cursor iterator 422 */ 423 public CursorableLinkedList.Cursor<E> cursor() { 424 return cursor(0); 425 } 426 427 /** 428 * Returns a {@link Cursor} for iterating through the elements of this list 429 * starting from a specified index. 430 * <p> 431 * A {@code Cursor} is a {@code ListIterator} with an additional 432 * {@code close()} method. Calling this method immediately discards the 433 * references to the cursor. If it is not called, then the garbage collector 434 * will still remove the reference as it is held via a {@code WeakReference}. 435 * <p> 436 * The cursor enables iteration and list changes to occur in any order without 437 * invalidating the iterator (from one thread). When elements are added to the 438 * list, an event is fired to all active cursors enabling them to adjust to the 439 * change in the list. 440 * <p> 441 * When the "current" (i.e., last returned by {@link ListIterator#next} 442 * or {@link ListIterator#previous}) element of the list is removed, 443 * the cursor automatically adjusts to the change (invalidating the 444 * last returned value such that it cannot be removed). 445 * <p> 446 * The {@link #listIterator(int)} method returns the same as this method, and can 447 * be cast to a {@code Cursor} if the {@code close} method is required. 448 * 449 * @param fromIndex The index to start from 450 * @return A new cursor iterator 451 * @throws IndexOutOfBoundsException if the index is out of range 452 * (index < 0 || index > size()). 453 */ 454 public CursorableLinkedList.Cursor<E> cursor(final int fromIndex) { 455 final Cursor<E> cursor = new Cursor<>(this, fromIndex); 456 registerCursor(cursor); 457 return cursor; 458 } 459 460 /** 461 * The equivalent of a default constructor called 462 * by any constructor and by {@code readObject}. 463 */ 464 @Override 465 protected void init() { 466 super.init(); 467 cursors = new ArrayList<>(); 468 } 469 470 /** 471 * Returns an iterator that does <strong>not</strong> support concurrent modification. 472 * <p> 473 * If the underlying list is modified while iterating using this iterator 474 * a ConcurrentModificationException will occur. 475 * The cursor behavior is available via {@link #listIterator()}. 476 * 477 * @return A new iterator that does <strong>not</strong> support concurrent modification 478 */ 479 @Override 480 public Iterator<E> iterator() { 481 return super.listIterator(0); 482 } 483 484 /** 485 * Returns a cursor iterator that allows changes to the underlying list in parallel. 486 * <p> 487 * The cursor enables iteration and list changes to occur in any order without 488 * invalidating the iterator (from one thread). When elements are added to the 489 * list, an event is fired to all active cursors enabling them to adjust to the 490 * change in the list. 491 * <p> 492 * When the "current" (i.e., last returned by {@link ListIterator#next} 493 * or {@link ListIterator#previous}) element of the list is removed, 494 * the cursor automatically adjusts to the change (invalidating the 495 * last returned value such that it cannot be removed). 496 * 497 * @return A new cursor iterator 498 */ 499 @Override 500 public ListIterator<E> listIterator() { 501 return cursor(0); 502 } 503 504 /** 505 * Returns a cursor iterator that allows changes to the underlying list in parallel. 506 * <p> 507 * The cursor enables iteration and list changes to occur in any order without 508 * invalidating the iterator (from one thread). When elements are added to the 509 * list, an event is fired to all active cursors enabling them to adjust to the 510 * change in the list. 511 * <p> 512 * When the "current" (i.e., last returned by {@link ListIterator#next} 513 * or {@link ListIterator#previous}) element of the list is removed, 514 * the cursor automatically adjusts to the change (invalidating the 515 * last returned value such that it cannot be removed). 516 * 517 * @param fromIndex The index to start from 518 * @return A new cursor iterator 519 */ 520 @Override 521 public ListIterator<E> listIterator(final int fromIndex) { 522 return cursor(fromIndex); 523 } 524 525 /** 526 * Deserializes the data held in this object to the stream specified. 527 * 528 * @param in The input stream 529 * @throws IOException Thrown if an error occurs while reading from the stream 530 * @throws ClassNotFoundException if an object read from the stream cannot be loaded 531 */ 532 private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException { 533 in.defaultReadObject(); 534 doReadObject(in); 535 } 536 537 /** 538 * Registers a cursor to be notified of changes to this list. 539 * 540 * @param cursor The cursor to register 541 */ 542 protected void registerCursor(final Cursor<E> cursor) { 543 // We take this opportunity to clean the cursors list 544 // of WeakReference objects to garbage-collected cursors. 545 cursors.removeIf(ref -> ref.get() == null); 546 cursors.add(new WeakReference<>(cursor)); 547 } 548 549 /** 550 * Removes all nodes by iteration. 551 */ 552 @Override 553 protected void removeAllNodes() { 554 if (!isEmpty()) { 555 // superclass implementation would break all the iterators 556 final Iterator<E> it = iterator(); 557 while (it.hasNext()) { 558 it.next(); 559 it.remove(); 560 } 561 } 562 } 563 564 /** 565 * Removes the specified node from the list. 566 * 567 * @param node The node to remove 568 * @throws NullPointerException if {@code node} is null 569 */ 570 @Override 571 protected void removeNode(final Node<E> node) { 572 super.removeNode(node); 573 broadcastNodeRemoved(node); 574 } 575 576 /** 577 * Deregisters a cursor from the list to be notified of changes. 578 * 579 * @param cursor The cursor to deregister 580 */ 581 protected void unregisterCursor(final Cursor<E> cursor) { 582 for (final Iterator<WeakReference<Cursor<E>>> it = cursors.iterator(); it.hasNext();) { 583 final WeakReference<Cursor<E>> ref = it.next(); 584 final Cursor<E> cur = ref.get(); 585 if (cur == null) { 586 // some other unrelated cursor object has been 587 // garbage-collected; let's take the opportunity to 588 // clean up the cursors list anyway. 589 it.remove(); 590 } else if (cur == cursor) { 591 ref.clear(); 592 it.remove(); 593 break; 594 } 595 } 596 } 597 598 /** 599 * Updates the node with a new value. 600 * This implementation sets the value on the node. 601 * Subclasses can override this to record the change. 602 * 603 * @param node node to update 604 * @param value new value of the node 605 */ 606 @Override 607 protected void updateNode(final Node<E> node, final E value) { 608 super.updateNode(node, value); 609 broadcastNodeChanged(node); 610 } 611 612 /** 613 * Serializes this object to an ObjectOutputStream. 614 * 615 * @param out The target ObjectOutputStream. 616 * @throws IOException thrown when an I/O errors occur writing to the target stream. 617 */ 618 private void writeObject(final ObjectOutputStream out) throws IOException { 619 out.defaultWriteObject(); 620 doWriteObject(out); 621 } 622 623}