|
Quick Lists
|
|
Bug ID:
|
6463989
|
|
Votes
|
0
|
|
Synopsis
|
(coll) Provide Iterable utility methods and light-weight Iterable implementations
|
|
Category
|
java:classes_util
|
|
Reported Against
|
|
|
Release Fixed
|
|
|
State
|
5-Cause Known,
request for enhancement
|
|
Priority:
|
4-Low
|
|
Related Bugs
|
6431636
|
|
Submit Date
|
25-AUG-2006
|
|
Description
|
A DESCRIPTION OF THE REQUEST :
In Java 5, the Iterable class is a compelling option when settling on a type to use for passing lists between methods or classes. Unlike arrays, Iterables can be safely used with parameterized types, and are easily concatenated. Unlike Collections, Iterables are light-weight to implement and make explicit the frequent assumption that the list will not be modified.
Unfortunately, support in the APIs for arbitrary Iterables is almost nonexistent. The following operations and types (a few of which have been mentioned by others in RFEs) would be quite useful. Except where noted, these operations are meant to be defined lazily -- if Iterable B is defined in terms of Iterable A, then subsequent changes to A are reflected in B.
- boolean isEmpty(Iterable<?>): Do "iterable.iterator().hasNext()"
- String toString(Iterable<?>): Make a String following the Collection.toString contract
- boolean equals(Iterable<?>, Iterable<?>): Equality, as defined by List.equals
- int hashCode(Iterable<?>): Hash code, as defined by List.hashCode
- int size(Iterable<?>): Compute the size (possibly O(n))
- <T> T last(Iterable<? extends T>): Get the last value (possibly O(n))
- <T> Iterable<T> compose(Iterable<? extends T>, Iterable<? extends T>): Concatenate two iterables
- <T> Iterable<T> compose(T, Iterable<? extends T>): Place a value at the front of an iterable
- <T> Iterable<T> compose(Iterable<? extends T>, T): Place a value at the end of an iterable
- <T> Iterable<T> empty(): Return an empty iterable
- <T> Iterable<T> filter(Iterable<? extends T>, Predicate<? super T>): Filter out members that don't conform to a predicate (presupposes the existence of a Predicate class)
- <S, T> Iterable<T> map(Iterable<? extends S>, Function<? super S, ? extends T>): Define a new iterable in terms of some operation applied to another (presupposes the existence of a Function class)
- <S, T> Iterable<Pair<S, T>> zip(Iterable<? extends S>, Iterable<? extends T>): Produce an iterable of pairs, given two iterables of the same length (presupposes the existence of a Pair class); useful in a for-each loop where two lists need to be traversed simultaneously
- Iterable<Integer> sequence(int first, int last): Create a sequence from first to last
- <T> Iterable<T> sequence(T, Function<? super T, ? extends T>): Create an infinite sequence as computed by a function (presupposes the existence of a Function class)
- <T> Iterable<T> sequence(T, Function<? super T, ? extends T>, int): Create a finite sequence as computed by a function (presupposes the existence of a Function class)
- <T> Iterable<T> immutable(Iterable<? extends T>): Wrap the given iterable in an immutable wrapper, preventing mutation via casting or Iterator.remove() (mutation can still be observed if the original is mutated)
- <T> Iterable<T> snapshot(Iterable<? extends T>): Create a copy of the given iterable as it now stands; later mutations will not be reflected in the result
- <T> Iterable<T> asIterable(Iterator<? extends T>): Create a one-shot iterable based on the given iterator (useful in a for-each loop)
- <T> Iterable<T> asIterable(T...): Convert an array to an iterable (can also be defined for all primitive array types); by using varargs, we also have a convenient way to define literal iterables, but this approach is limited by the fact that T here must be reifiable to be safe. The workaround is to define a number of makeIterable(T, T, T) methods, each with a different number of arguments, up to a reasonable amount (10, perhaps)
- Iterable<Character> asIterable(CharSequence): Treat a CharSequence (such as a String) as an Iterable
- <T> Iterator<T> asIterator(Enumeration<? extends T>): Convert an Enumeration to an *Iterator*. A departure from all the *Iterable* methods, but this would seem like an appropriate place for it.
- <T> List<T> asList(Iterable<? extends T>): Convert an Iterable to a List (by casting or by creating a new List)
- <T> Iterable<T> reverse(Iterable<? extends T>): Reverses the given iterable (can't be performed lazily)
- <T> Iterable<T> shuffle(Iterable<? extends T>, Random): Randomly shuffles the given values (not necessarily performed lazily; this could alternatively be defined on Collections, along with all the Collections.sort() methods)
- <T> Iterable<T> skipFirst(Iterable<? extends T>): Produce an iterable that doesn't contain the first value
- <T> Iterable<T> skipLast(Iterable<? extends T>): Produce an iterable that doesn't contain the last value
- <T> Iterable<T> truncate(Iterable<? extends T>, int): Produce an iterable with the first x elements
- interface SizedIterable<T> extends Iterable<T> { public int size(); }: Allows slightly more powerful access to the data structure, while remaining lightweight. Collection would implement this interface. All the above could be defined in terms of SizedIterables.
JUSTIFICATION :
While the necessity of each specific operation listed above is open to debate, the necessity of *some* form of a java.util.Iterables utility class seems clear. Otherwise, there is no reason to introduce the Iterable class in the first place -- the language could have just performed for-each iteration on Collections instead.
Iterable, or an interface like it, really should have been around when Collections were first defined with the concept of "unsupported operations." It is possible to do everything that might be done with an Iterable by simply using a deficient form of Collection that supports almost no operations. (AbstractSequentialList provides something like this, but requires the implementation of the more complicated ListIterator rather than just Iterator.) But resorting to such a scheme essentially defeats the purpose of static typing. If a method produces an Iterable, it really shouldn't claim to produce a Collection. And if a method does not need the extra facilities provided by Collection, it ought to be defined in terms of Iterable.
There are equivalents to many of the proposed operations in the Collection classes. But they are generally not implemented in as lightweight a manner as the Iterable versions could be. For example, iterable composition is a simple customer allocation, while composition of two collections is generally O(n) (n being the size of the second collection), because the second collection's values must be integrated into the first's data structures.
CUSTOMER SUBMITTED WORKAROUND :
An open-source implementation of these operations, along with others, is used by the DrJava project and can be found here:
<https://svn.sourceforge.net/svnroot/drjava/trunk/plt/src/edu/rice/cs/plt/iter/>
The javadocs are here:
<http://drjava.org/javadoc/plt/edu/rice/cs/plt/iter/package-summary.html>
Posted Date : 2006-08-25 11:59:29.0
|
|
Work Around
|
N/A
|
|
Evaluation
|
Historical design motivation can be found here:
http://java.sun.com/j2se/1.5.0/docs/guide/collections/designfaq.html#8
"# Why don't you provide a form of the addAll method that takes an Enumeration (or an Iterator)?
Because we don't believe in using Enumerations (or Iterators) as "poor man's collections." This was occasionally done in prior releases, but now that we have the Collection interface, it is the preferred way to pass around abstract collections of objects.
# Why don't the concrete implementations in the JDK have Enumeration (or Iterator) constructors?
Again, this is an instance of an Enumeration serving as a "poor man's collection" and we're trying to discourage that. Note however, that we strongly suggest that all concrete implementations should have constructors that take a Collection (and create a new Collection with the same elements). "
Posted Date : 2006-08-25 17:19:40.0
The submitter adds this interesting SDN comment:
-----------------------------------------------
The evaluation's reference to the Collections Design FAQ is useful, but the citations seem to miss the point. This is *not* a request to treat Iterators as Collections. *Iterables* are not *Iterators*. The problem with treating Iterators as Collections (as I understand it) is that Iterators contain state -- reading an Iterator changes its contents, and the full contents may only be traversed once. There is an implicit assumption, on the other hand, that reading a Collection does not change its contents. This assumption holds in the case of Iterables. (I *did* request a method that wraps an Iterator in an Iterable. If these comments are directed towards that one item, then they are relevant, but ignore the broader request.)
It's important to note that the FAQ was written *before* the introduction of the Iterable interface, and so makes no mention of it.
Some items from the FAQ that I think *are* relevant:
Q. "Why don't you support immutability directly in the core collection interfaces so that you can do away with optional operations (and UnsupportedOperationException)?"
A. [Summary] We can't get rid of UnsupportedOperationExceptions without an intractable number of variations on the core interfaces. It's better to keep it simple. [Iterable, however, provides a very simple interface representing an immutable collection. Providing Iterable utility methods does not require the definition of a large number of new interfaces.]
Q. "Why don't you provide an "apply" method in Collection to apply a given method ("upcall") to all the elements of the Collection?" [See my "map" request, above.]
and
Q. "Why didn't you provide a "Predicate" interface, and related methods (e.g., a method to find the first element in the Collection satisfying the predicate)?" [See my "filter" request, above.]
A. [Summary] If you want to execute code for each element of a collection, it's "easier" to just use a for loop. There is also the additional weight of defining the necessary interfaces for mapping functions and predicates. However, it's possible this functionality will be added later.
So, while the Collection Design FAQ provides some discussion on issues that would similarly arise in a discussion of an Iterable utility class, it does not address the key question of whether such a class would be useful at all. (If there were an FAQ about the choice to include "Iterable" in the API, that would be quite relevant. Did the designers intend to provide the interface without being able to do much of anything with objects of that type?)
-----------------------------------------------
I'd be interested in the opinion of the designers of the Collection Framework
(I am the current maintainer)
Note also that a "Closures" proposal is being considered for jdk7.
http://blogs.sun.com/roller/resources/ahe/closures.pdf
If that proposal is adopted, I think we will see many more
APIs added that take things like Predicates.
Posted Date : 2006-08-27 22:26:56.0
|
|
Comments
|
Submitted On 26-AUG-2006
daniel_l_smith
The evaluation's reference to the Collections Design FAQ is useful, but the citations seem to miss the point. This is *not* a request to treat Iterators as Collections. *Iterables* are not *Iterators*. The problem with treating Iterators as Collections (as I understand it) is that Iterators contain state -- reading an Iterator changes its contents, and the full contents may only be traversed once. There is an implicit assumption, on the other hand, that reading a Collection does not change its contents. This assumption holds in the case of Iterables. (I *did* request a method that wraps an Iterator in an Iterable. If these comments are directed towards that one item, then they are relevant, but ignore the broader request.)
It's important to note that the FAQ was written *before* the introduction of the Iterable interface, and so makes no mention of it.
Some items from the FAQ that I think *are* relevant:
Q. "Why don't you support immutability directly in the core collection interfaces so that you can do away with optional operations (and UnsupportedOperationException)?"
A. [Summary] We can't get rid of UnsupportedOperationExceptions without an intractable number of variations on the core interfaces. It's better to keep it simple. [Iterable, however, provides a very simple interface representing an immutable collection. Providing Iterable utility methods does not require the definition of a large number of new interfaces.]
Q. "Why don't you provide an "apply" method in Collection to apply a given method ("upcall") to all the elements of the Collection?" [See my "map" request, above.]
and
Q. "Why didn't you provide a "Predicate" interface, and related methods (e.g., a method to find the first element in the Collection satisfying the predicate)?" [See my "filter" request, above.]
A. [Summary] If you want to execute code for each element of a collection, it's "easier" to just use a for loop. There is also the additional weight of defining the necessary interfaces for mapping functions and predicates. However, it's possible this functionality will be added later.
So, while the Collection Design FAQ provides some discussion on issues that would similarly arise in a discussion of an Iterable utility class, it does not address the key question of whether such a class would be useful at all. (If there were an FAQ about the choice to include "Iterable" in the API, that would be quite relevant. Did the designers intend to provide the interface without being able to do much of anything with objects of that type?)
Submitted On 12-FEB-2008
Micha_Riser
an isEmpty() method on the Iterable would be very helpful in many circumstances
Submitted On 02-JUN-2009
Note that you are welcome to pilfer code from the google collections library:
http://code.google.com/p/google-collections/source/browse/trunk/src/com/google/common/collect/Iterables.java
PLEASE NOTE: JDK6 is formerly known as Project Mustang
|
|
|
 |