views:

686

answers:

10

What are the considerations of using Iterable<T> vs. Collection<T> in Java?

For example, consider implementing a type that is primarily concerned with containing a collection of Foos, and some associated metadata. The constructor of this type allows one-time initialisation of the object list. (The metadata can be set later.) What type should this constructor accept? Iterable<Foo>, or Collection<Foo>?

What are the considerations for this decision?

Following the pattern set forth by library types such as ArrayList (which can be initialised from any Collection, but not an Iterable) would lead me to use Collection<Foo>.

But why not accept Iterable<Foo>, given that this is is sufficient for the initialisation needs? Why demand a higher level of functionality (Collection) from the consumer, than what is strictly necessary (Iterable)?

+4  A: 

According to the principle of least surprise, you should emulate the Java collection pattern and take a Collection constructor arg. It will make the people who come after you slightly less puzzled.

Paul McKenzie
I disagree - Iterable still lets you take Collections as arguments, but as John says allows you to pass in other classes that let you step over their elements. If this is all the functionality you need, it's a much better option. Your Javadocs should document the argument anyway, and if the people coming after you are puzzled by core Java classes there are probably deeper issues. ;-)
Andrzej Doyle
I'm with Paul. I hardly ever see Iterable, in our own classes or in libraries. I see Collection more, and List most frequently. I think there is logic behind the choice of Iterable, but I think current idiom is Collections. Maybe it should be Iterables, and maybe it will be, someday, but for now, I would be less surprised by a Collection.
Carl Manaster
** BOO!! **
Tom Hawtin - tackline
A: 

In a situation where you may me streaming, like reading a big XML file from a socket or off disk. A Collection doesn't make sense but you could create an iterator to go over the nodes and implement hasNext(), next() and (optionally) remove().

Clint
+8  A: 

Many of the collection types existed before Iterable<T> (which was only introduced in 1.5) - there was little reason to add a constructor to accept Iterable<T> as well as Collection<T> but changing the existing constructor would have been a breaking change.

Personally I would use Iterable<T> if that allows you to do everything you want it to. It's more flexible for callers, and in particular it lets you do relatively easy filtering/projection/etc using the Google Java Collections (and no doubt similar libraries).

Jon Skeet
Downvoter: care to provide a reason?
Jon Skeet
You say it would be a "breaking change." What would break if (say) the ArrayList<T> constructor parameter was changed from Collection<T> to Iterable<T>?
finnw
A: 

You're correct, as it's considered good practice to ask for the most general form of what you need.

Steve B.
+3  A: 

Use the most general interface that you can. Since all you're going to do is iterate, then I would say Iterable is the way to go (since it allows lazy iterators, etc.). You don't care where the iterator is coming from, so don't restrict it more than you have to.

Michael Myers
+3  A: 

If you go for Collection, then your class can be initialised from a collection only, if you go for Iterable, then you can initialise from either collection or iterable.

As the effort and performance for both is going to be the same, it makes total sense to accept Iterable in the constructor.

Patrick McDonald
+3  A: 

An Iterable produces Iterator objects. An Iterator object, by definition, iterates. Notice, that the Iterator interface makes no promise as to how many times next() can be called before hasNext() returns false. An Iterator could possibly iterate over Integer.MAX_VALUE + 1 values before its hasNext() method returns false.

However, a Collection is a special form of Iterable. Because a Collection cannot have more than Integer.MAX_VALUE elements (by virtue of the size() method), it is naturally presumed that its Iterator objects will not iterate over this many elements.

Therefore, by accepting a Collection rather than an Iterable, your class can have some guarantee over how many elements are being passed in. This is especially desirable if your class is itself a Collection.

Just my two cents...

Adam Paynter
That disallows fairly *unreasonable* iterables at the expense of also disallowing non-collection, entirely reasonable iterables.
Jon Skeet
+2  A: 

Some constructors, e.g. ArrayList(Collection c), use the toArray() method of Collection for efficiency.

starblue
A: 

What about synchronization? If you are going to iterate over a collection you need to acquire a lock on it before getting the iterator.

  synchronized(myList) {
      Iterator iter = myList.iterator();
      while (iter.hasNext()) {
          iter.next();
          ...
      }
  }

If you receive the Iterator instead of the Collection you are not sure if your thread owns the object.

rodrigoap
myList could implement Itera<i>ble</i> and still get you what you need. You are right about passing an Iterat<i>or</i> directly, though.
Yishai
This isn't a good practice. Many core Java collections are designed for single-threaded use; that is, they don't synchronized on themselves or any lock object. So, it is up to callers to perform the necessary synchronization. While you have have synchronized on the collection itself here, it's difficult to enforce that all users of that instance will do the same. In general, if multiple threads are sharing memory, a suitable collection from java.util.concurrent is preferable.
erickson
A: 

See "Why so much emphasis on Iterators and Iterables?" on the Google Collection FAQ for a decent argument for preferring Iterators, especially when dealing with a lot of data. One analogy that might help is to think the difference between forward-only read-only cursors and scrollable cursors.

Rick