为什么数组不能分配给Iterable?


186

使用Java5,我们可以编写:

Foo[] foos = ...
for (Foo foo : foos) 

或仅在for循环中使用Iterable。这非常方便。

但是,您不能像这样编写可迭代的通用方法:

public void bar(Iterable<Foo> foos) { .. }

并使用数组调用它,因为它不是Iterable:

Foo[] foos = { .. };
bar(foos);  // compile time error 

我想知道这个设计决定背后的原因。


8
我想Arrays.asList足够好
dfa

17
这是一个哲学问题
dfa

2
在Java 5+中处理数组的一个很好的理由是varargs方法。
杰夫·沃克

2
@Torsten:是的,但是如果将其传递给接受Iterable的方法,则可能无论如何都不会进行任何更改。
迈克尔·迈尔斯

5
实际上,Arrays.asList 不够好,因为它不适用于基本类型的数组。通用迭代(装箱)原始类型元素的唯一内置方法是使用反射,使用java.lang.reflect.Array,但是其性能较弱。但是,您可以编写自己的迭代器(或List实现!)来包装基本类型的数组。
Boann 2013年

Answers:


78

数组可以实现接口(Cloneablejava.io.Serializable)。那为什么不Iterable呢?我猜想Iterable强制添加iterator方法,而数组不实现方法。char[]甚至不覆盖toString。无论如何,应将引用数组视为不理想的-使用Lists。如dfa注释所示,Arrays.asList将为您明确进行转换。

(话虽如此,您可以调用clone数组。)


23
>“ ...并且数组不实现方法。” 我认为这是另一个哲学问题。数组从来都不是原始类型,而Java哲学则认为“一切都是对象(原始类型除外)”。那么,为什么有数组不执行方法,即使从一开始就有大量的操作想要使用数组。哦,是的,很遗憾,事后看来,数组是唯一的强类型集合。
fatuhoku

2
如果数组中有数据,则可能正在执行低级的,对性能至关重要的工作,例如处理从流读取的byte []。无法迭代数组的原因可能是Java泛型不支持将基元用作类型参数,如下面的@Gareth所述。
德鲁·诺阿克斯

2
@FatuHoku暗示泛型是一个遗憾,事后看来是不正确的。泛型的合意性一直受到赞赏。数组不是原语(我不是说它们是原语),但是它们是低级的。您想对数组做的一件事就是将它们用作类似矢量的结构的实现细节。
Tom Hawtin-大头钉

1
Iterator<T>还要求remove(T),尽管允许抛出一个UnsupportedOperationException
wchargin

数组确实实现方法:它们实现的所有方法java.lang.Object
mhsmith '17

59

该数组是一个对象,但可能不是。该数组可能包含一个像int这样的基本类型,Iterable无法应付。至少我是这样认为的。


3
这意味着为了支持该Iterable接口,必须将原始数组专用于使用包装器类。不过,这都不是什么大不了的事情,因为无论如何类型参数都是伪造的。
thejoshwolfe 2011年

8
这不会阻止对象数组实现Iterable。这也不会阻止原始数组为包装类型实现Iterable。
Boann

1
自动装箱可以解决这个问题
TimBüthe13年

我认为这是正确的原因。除非泛型支持原始类型(例如,List<int>而不是List<Integer>等),否则它对于原始类型的数组将无法令人满意。骇客可以使用包装器来完成,但会降低性能-更重要的是-如果此骇客被盗用,将来将无法在Java中正确实现(例如int[].iterator()将无法将永远被锁定以返回Iterator<Integer>而不是Iterator<int>)。也许,即将到来的Java值类型+通用专业化(项目valhalla)将使数组实现Iterable
比贾克(Bjarke)'18年

16

数组应该支持Iterable,但不应该支持。出于同样的原因,.NET数组不支持允许按位置进行只读随机访问的接口(没有这样的接口定义为标准接口)。从根本上说,框架之间经常存在令人讨厌的小差距,这不值得任何人修复。我们能否以某种最佳方式自己修复它们并不重要,但通常我们做不到。

更新:为了公平起见,我提到了.NET数组不支持按位置支持随机访问的接口(另请参见我的评论)。但是在.NET 4.5中,已经定义了确切的接口,并由数组和List<T>类支持:

IReadOnlyList<int> a = new[] {1, 2, 3, 4};
IReadOnlyList<int> b = new List<int> { 1, 2, 3, 4 };

由于可变列表接口IList<T>不继承,因此一切还不是很完美IReadOnlyList<T>

IList<int> c = new List<int> { 1, 2, 3, 4 };
IReadOnlyList<int> d = c; // error

也许通过这种更改可能会向后兼容。

如果Java的较新版本在类似方面取得了任何进展,请在评论中了解!:)


8
.NET数组实现IList接口
汤姆·吉伦

2
@Aphid-我说过只读随机访问。IList<T>公开用于修改的操作。如果这将是巨大的IList<T>继承像一个IReadonlyList<T>接口,这将刚CountT this[int]和继承IEnumerable<T>(已经支持只读枚举)。另一个很棒的事情是用于获取逆序枚举器的接口,Reverse扩展方法可以查询该接口(就像Count扩展方法查询ICollection以优化自身一样。)
Daniel Earwicker 2011年

是的,如果事情是这样设计的,那就更好了。IList接口确实定义了IsReadOnly和IsFixedSize属性,这些属性由数组适当地实现。但是,这一直令我感到很糟糕,因为它没有提供编译时检查来确保给定的列表实际上是只读的,而且我很少看到检查这些属性的代码。
汤姆·吉伦

1
在.NET数组实现IListICollection因为.NET 1.1,IList<T>ICollection<T>自.NET 2.0。这是Java远远落后于竞争对手的另一种情况。
阿米尔·阿比里

@TomGillen:我最大的问题IList是它不提供更多可查询的属性。我想说,对于初学者来说,适当的设置应该包括IsUpdateable,IsResizable,IsReadOnly,IsFixedSize和ExistingElementsAreImmutable。引用的代码是否可以在不进行类型转换的情况下修改列表的问题与持有不应该修改的列表的引用的代码可以安全地与外部代码直接共享该引用的问题分开它可以安全地假设列表的某些方面永远不会改变。
supercat 2014年

14

不幸的是,数组还不够class。他们没有实施Iterable接口。

虽然数组现在是实现Clonable和Serializable的对象,但我相信 数组不是通常意义上的对象,并且不实现接口。

之所以可以在for-each循环中使用它们,是因为Sun在数组中添加了一些语法糖(这是一种特殊情况)。

由于数组在Java 1中最初是“几乎对象”,因此要在Java 中使它们成为真实对象实在是太过激烈了。


14
仍然有for-each循环的糖,那么为什么Iterable不能有糖呢?
Michael Myers

8
@mmyers:for-each中使用的糖是编译时糖。这比虚拟机糖容易得多。话虽如此,.NET阵列在该领域要好得多……
Jon Skeet

12
数组可以实现接口。它们实现CloneableSerializable接口。
notnoop

34
Java数组在所有意义上都是对象。请删除那部分错误信息。他们只是不实现Iterable。
ykaganovich

5
数组是一个对象。它支持有用的:P方法,例如wait(),wait(n),wait(n,m),notify(),notifyAll(),finalize(),toString()的无意义实现。唯一有用的方法是getClass() 。
彼得·劳瑞

1

编译器实际上将for each数组上的转换为简单的for带有计数器变量循环。

编译以下

public void doArrayForEach() {
    int[] ints = new int[5];

    for(int i : ints) {
        System.out.println(i);
    }
}

然后反编译.class文件产生

public void doArrayForEach() {
    int[] ints = new int[5];
    int[] var2 = ints;
    int var3 = ints.length;

    for(int var4 = 0; var4 < var3; ++var4) {
        int i = var2[var4];
        System.out.println(i);
    }
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.