简体   繁体   中英

Reset Iterator position to 0 in a nested while-loop

I am performing iteration on two lists.

// events and items are two lists.

Iterator<Event> eventIterator = events.iterator();
Iterator<EventItem> itemIterator = items.iterator();

while (eventIterator.hasNext()) {

    Event event = eventIterator.next();

    while (itemIterator.hasNext()) {

        EventItem item = itemIterator.next();

        if (event.getId().equals(item.getEventId())) {

            // CLAIMED
            itemIterator.remove();
        }
    }

    // PROBLEM IS HERE.

}

Problem:

I looped through itemIterator to the end, and also removed the items from itemIterator if an item gets claimed. But, when next iteration on eventIterator will run, itemIterator is already at its end.

How can I reset itemIterator to position 0 without reinitiating it using items.iterator() because that will bring back items removed from itemIterator for next iteration.

One item from itemIterator can only be claimed by one event from eventIterator . So, it doesn't make sense for me to keep that item in iteration after it is claimed. Hence, I remove the item from itemIterator .

I am open to alternatives if they decrease the number of iterations than the traditional for-each loop.

How can I reset itemIterator to position 0 without reinitiating it using items.iterator() because that will bring back items removed from itemIterator for next iteration.

No, it won't. itemIterator.remove() removes the item from the collection you got the iterator from, not just the iterator. If you get a new iterator from that collection, it won't have that item on it anymore; you removed it. From the JavaDoc :

Removes from the underlying collection the last element returned by this iterator (optional operation). This method can be called only once per call to next() .

(my emphasis)

You can't reset an iterator; just get a new one, within the while .

Iterator<Event> eventIterator = events.iterator();

while (eventIterator.hasNext()) {
    Event event = eventIterator.next();

    Iterator<EventItem> itemIterator = items.iterator();
    while (itemIterator.hasNext()) {
        EventItem item = itemIterator.next();
        if (event.getId().equals(item.getEventId())) {
            // CLAIMED
            itemIterator.remove();
        }
    }

    // If you need to loop a second time for some reason:
    itemIterator = items.iterator();
    // ...
}

Using nested loops like this is O(N * M) and is expensive, but it's also verbose which obscures the purpose of what you are trying to achieve. I suggest you use streams from Java 8.

// get all the event's ids
Set<String> eventIds = events.stream()
        .map(Event::getId)
        .collect(Collectors.toSet());

// remove the entries from items with a matching id.
items.removeIf(i -> eventIds.contains(i.getEventId()));

This has a time complexity of O(N + M).

And, removed/claimed items get added to another list (I call it EventDto, data transfer object)

You can do that by building the list first.

// remove the entries from items with a matching id.
List<EventItem> toMove = items.stream()
                              .filter(i -> eventIds.contains(i.getEventId()))
                              .collect(Collectors.toList());
items.removeAll(toMove);
anotherList.addAll(toMove);

This might be more efficient if the id was used as the key of a map

eg

Map<String, Event> events = ... // events keyed by id
Map<String, EventItem> items = ... // event items keys by eventId

events.keySet().removeAll(items.keySet());

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM