4

I'm iterating a java.util.LinkedList and in some cases I add an element to it.

LinkedList<Schedule> queue = new LinkedList<Schedule>(schedules);
ListIterator<Schedule> iterator = queue.listIterator();
while (iterator.hasNext()) {
    Schedule schedule = iterator.next();
    if(condition)
        iterator.add(new Schedule());
} 

The problem is that if I start for example with one item, the new item is added before the next iterator.next() call and the iteration exits.

How can I append the item at the end of the LinkedList while iterating? Please don't tell me to use another list and iterate it after the first because it doesn't solve my problem correctly.

Betlista
  • 10,327
  • 13
  • 69
  • 110
Alessandro Dionisi
  • 2,494
  • 4
  • 33
  • 37

5 Answers5

1

If you cannot use another list, you could solve your problem by keeping a count of the number of elements you processed via the iterator and compare that to the original size of the list: all new element will be at the end of the list, so you can end your loop when you have reached the original size.

LinkedList<Schedule> queue = new LinkedList<Schedule>(schedules);
int origSize = queue.size();
int currCount = 0;
ListIterator<Schedule> iterator = queue.listIterator();
while (iterator.hasNext()) {
  ++currCount;
  if (currCount >= origSize) {
    break; // reached the end of the original collection
  }
  Schedule schedule = iterator.next();
  if(condition)
    iterator.add(new Schedule());
} 

You could also use an extra list to keep track of the new elements and add that to the original list after the processing is over:

LinkedList<Schedule> queue = new LinkedList<Schedule>(schedules);
LinkedList<Schedule> addQueue = new LinkedList<Schedule>();
ListIterator<Schedule> iterator = queue.listIterator();
while (iterator.hasNext()) {
  Schedule schedule = iterator.next();
  if(condition)
    addQueue.add(new Schedule());
} 
queue.addAll(addQueue);

Also, note that iterator.add()

Inserts the specified element into the list (optional operation). The element is inserted immediately before the next element that would be returned by next, if any, and after the next element that would be returned by previous, if any. (If the list contains no elements, the new element becomes the sole element on the list.) The new element is inserted before the implicit cursor: a subsequent call to next would be unaffected, and a subsequent call to previous would return the new element. (This call increases by one the value that would be returned by a call to nextIndex or previousIndex.)

so if you have more than one elements in the list, it will not add the new ones to the end, but between the current one and the one returned by next(). If you indeed want to place the new elements at the end of the list, use queue.add(...)

In general, it is not advisable to modify a collection while traversing it via an iterator, so I suggest you use the second approach (collect the extra elements in a separate list and add them to the original at the end)

Attila
  • 28,265
  • 3
  • 46
  • 55
  • What you are saying is true but the problem is that I want to add while iterating because I need to process also new added elements during the iteration. So maybe I must find a workaround using other lists. – Alessandro Dionisi Apr 27 '12 at 13:14
  • If you want to access the just inserted new element, you can call `iterator.previous()` (see quoted text in answer) – Attila Apr 27 '12 at 13:57
1

Assuming you don't have a hard requirement to use an iterator then you can just shelve it and 'iterate' over the list by index instead:

LinkedList<Schedule> list;

for (int i = 0; i < list.size(); i++) {
   final Schedule schedule = list.get(i);
   if(condition)
       list.add(new Schedule());
}
Perception
  • 79,279
  • 19
  • 185
  • 195
  • 3
    +1: But `list.get(i)` is not generally good with linked lists. – Don Roby Apr 27 '12 at 11:52
  • Yes I know, adding to a list while iterating is generally a bad idea. But this is probably the closest the OP is going to come to a (straight-forward) solution. – Perception Apr 27 '12 at 12:11
1

As others have suggested, there is no efficient, off-the-shelf support for end-insertion during iteration in the current Collections framework. One suggestion involved rewriting the iterator. But I say, why not go a couple steps further?

  1. Use reflection to modify the accessibility of the Node class, which gives you references to the double links, so you don't need to start iteration over with get(index), which should not be used in performant code.

  2. Inherit/derive from LinkedList and override judiciously.

  3. This is a no-brainer, but in the same spirit as 2 above, since the JDK is open-source, borrow the source as needed and write your own implementation.

In either case, I really feel this feature should be provided by Java LinkedList API.

flow2k
  • 3,999
  • 40
  • 55
0

How can I append the item at the end of the LinkedList while iterating?

public void addWork(Scheduler scheduler)
{
 synchronized(scheduler)
 {
  queue.addLast(scheduler);
 }
}

and you can use queue.removeFirst() to deal with item in the queue from top-to-down.

public synchronized Scheduler getWork()
{
  return queue.removeFirst();
}

Edited.

Bitmap
  • 12,402
  • 16
  • 64
  • 91
-1

The requirements to add while iterating and include the added items in the iteration can only be met if you don't use an iterator for iteration since there is no way to recompute the state of your iterator every time an element is added. If you accept the less efficient get method to do your iteration, the problem is trivial. For example

LinkedList<Schedule> queue = new LinkedList<Schedule>(){{add(new Schedule());add(new Schedule());add(new Schedule());}};
int i = 0;
// queue.size() is evaluated every iteration
while (i < queue.size()) {
    Schedule schedule = queue.get(i);
    if(i++ % 2 == 0)
        queue.add(new Schedule());
}
System.out.println(queue.size());

prints 6 as expected.

MilesHampson
  • 2,069
  • 24
  • 43