cllanjim / google-collections

Automatically exported from code.google.com/p/google-collections
Apache License 2.0
0 stars 0 forks source link

Request: add exhaust() and extract() methods to Iterators and Iterables #241

Closed GoogleCodeExporter closed 8 years ago

GoogleCodeExporter commented 8 years ago
I propose to add two new methods exhaust() and extract() to both the
Iterators and Iterables utilities.

Iterators.exhaust() is used to eagerly remove all the remaing elements from
the passed-in Iterator and buffer/provide them on the returned instance.
This has two effects: (a) the original Iterator has finished iteration and
can thus no longer be used to modify the backing collection, and (b) the
backing collection will supposedly be empty and ready for reuse, all by the
time the method returns.

Iterables.exhaust() could do essentially the same (clearing the backing
collection, buffering/providing the elements in a new Iterable),
OR
it could return an Iterable wrapper that delegates to Iterators.exhaust()
on every call to iterator(), effectively allowing for an automatic
exhaustion of the backing collection, in case new elements will have been
added (I prefer this behaviour -- maybe we can have both).

The exhaust() method allows a client to conveniently clear and reuse its
collections BEFORE actually processing the elements. The cleared collection
may even recieve new elements within the same loop. It is also useful if
the processing of elements is expensive and you don't want to block the
collection. Today, you would have to create your own temporary storage,
which is sometimes cumbersome and even inefficient if you are only looking
for an Iterable.

Iterators.extract() is used to automatically remove upcoming elements from
the passed-in Iterator on each call to next(), effectivly "popping" them
off the backing collection as-you-go. Once the iteration has finished all
elements will have been removed.

Iterables.extract() will simply wrap and delegate to Iterators.extract().

The extract() method allows to conveniently remove elements from a
collection within an enhanced for-loop.

Here is a sample implementation (pre-existing methods omitted):

class Iterators {
  public static <T> Iterator<T> exhaust(final Iterator<T> iterator) {
    List<T> list = Lists.newLinkedList();
    Iterators.addAll(list, Iterators.extract(iterator));
    return list.iterator();
  }

  public static <T> Iterator<T> extract(final Iterator<T> iterator) {
    return new Iterator<T>() {
      public boolean hasNext() { return iterator.hasNext(); }
      public T next() {
        T next = iterator.next();
        iterator.remove(); // what about an exception?
        return next;
      }
      public void remove() { /* no-op */ }
    }
  }
}

class Iterables {
  public static <T> Iterable<T> exhaust(final Iterable<T> iterable) {
    List<T> list = Lists.newLinkedList();
    Iterables.addAll(list, Iterables.extract(iterable));
    return list;
  }

  // I prefer this, because of the parallelism to Iterables.extract()
  public static <T> Iterable<T> exhaustWrap(final Iterable<T> iterable) {
    return new Iterable<T>() {
      public Iterator<T> iterator() {
        return Iterators.exhaust(iterable.iterator());
      }
    }
  }

  public static <T> Iterable<T> extract(final Iterable<T> iterable) {
    return new Iterable<T>() {
      public Iterator<T> iterator() {
        return Iterators.extract(iterable.iterator());
      }
    }
  }
}

Here are some sample use-cases:

// replace elements in a random collection
{
   // some kind of Iterable<Foo>, but not a Collection<Foo>
   IterableContainer<Foo> container = ...

   // old
   Collection<Foo> temp = new LinkedList<Foo>();
   for (Iterator<Foo> iterator = container.iterator();
       iterator.hasNext(); iterator.remove())
     temp.add( iterator.next() );
   for (Foo foo : temp)
     container.insert( replace(foo) );

   // new
   for (Foo foo : Iterables.exhaust(container))
     container.insert( replace(foo) );

}

// pop off elements as-you-go
{
  Iterable<Waste> garbage = ...

  // old
  for (Iterator<Waste> iterator = garbage.iterator();
      iterator.hasNext(); iterator.remove())
  {
    Waste waste = iterator.next();
    dispose( waste.getRecycle(), waste.getResidual() );
  }

  // new
  for (Waste waste : Iterables.extract(garbage))
    dispose( waste.getRecycle(), waste.getResidual() );
}

// integration with other google-collections methods
{
  List<Foo> list = ...

  // filter "in place"
  Iterables.addAll(list,
    Iterables.filter(Iterables.exhaust(list), someFilter));

  // clear and return last
  Foo last = Iterables.getLast(Iterables.extract(list));
  ...
}

The main purpose of the proposed methods is to clear existing collections
and have them ready for reuse in a neat and convenient way. This is a
missing feature in google-collections.

Thank you for reading.

Cheers!

---

This is a rephrase of my previous post to the user mailing list:

http://groups.google.com/group/google-collections-users/browse_thread/thread/9af
e822079faf021/

Original issue reported on code.google.com by Robin...@gmail.com on 18 Sep 2009 at 1:19

GoogleCodeExporter commented 8 years ago

Original comment by kevin...@gmail.com on 18 Sep 2009 at 5:04

GoogleCodeExporter commented 8 years ago
This issue has been moved to the Guava project (keeping the same id number). 
Simply replace 'google-collections' with 'guava-libraries' in your address 
bar and it should take you there.

Original comment by kevinb@google.com on 5 Jan 2010 at 11:09