在 Java 中,什么时候应该接受 Iterable < T > vs. Collection < T > 的参数?

在 Java 中使用 Iterable<T>Collection<T>的考虑是什么?

例如,考虑实现一个主要关注包含 Foo集合和一些相关元数据的类型。此类型的构造函数允许一次性初始化对象列表。(元数据可以稍后设置。)这个构造函数应该接受什么类型?Iterable<Foo>还是 Collection<Foo>

这个决定的考虑因素是什么?

遵循由库类型设定的模式,如 ArrayList(它可以从任何 Collection初始化,但是 没有Iterable)将导致我使用 Collection<Foo>

但是为什么不接受 Iterable<Foo>,因为这已经足够满足初始化的需要了?为什么要求消费者提供更高级别的功能(Collection) ,而不是严格必要的功能(Iterable) ?

29998 次浏览

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.

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).

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

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.

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.

An Iterable produces Iterator objects. An Iterator object, by definition, Iterator1. 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...

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

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.

Users of Spring Data JPA will find that the Repositories return collections of type Iterable<T>.

On projects I've worked on in the past that use Spring, I've found that the need to operate on a Collection after retrieval has often dictated that Iterable<T> is used in the business layer rather than Collection<T> in order to select an Object T from the collection.

All Collections are Iterable (that is the interfaces which extend the Collection interface, so not Map!), so using Iterable in the business layer is merely a case of referring to a Collection by its super type and still allows the use of for-each to iterate.

If you need to manipulate the contents of a collection, a convenience method would allow you to populate a new Collection, so you can make use of contains(), remove(), etc with the original collection data.

Alternatively, convenience methods are provided for this purpose by popular third party APIs such as Google Guava and Apache Commons.