API Java 5及以上版本:我应该返回数组还是集合?

33
最佳实践:始终返回一个____,而不是一个____的精神下,我在即将进行的从JDK1.4.2迁移到JDK5及更高版本中面临着类似的问题(是的,我知道JDK1.4.2已经停止更新了!;-))。
对于返回集合的函数(这些集合不是简单的属性集合),我总是更喜欢(在JDK1.4.2中)返回一个数组而不是一个通用列表,因为:
- 它强制执行返回类型(MyObject[]而不是对象列表,更加静态--如编译--级别上更加类型安全) - 它建议返回集合的“只读”特性(尽管这不像C#中的“只读”关键字那么严格,向集合添加元素更加复杂)。这并不意味着它是“不可变的”,因为数组内部的引用仍然可以被修改...
当然,我总是创建这个返回的数组(我不会暴露任何“内部”数组)
现在,在JDK5及更高版本中,如果我想的话,我可以使用List<MyObject>

在使用Java5编程时,为什么选择返回MyObject[]而不是List或Collection<MyObject>

如果使用Collection<MyObject>,是否可以:

  • 强制返回的集合为只读属性?(无法add()remove()
  • 强制返回的集合为不可变属性?(甚至该集合的引用也不能被修改)

注:JavaGenericFAQ中没有涉及此问题。

4个回答

37
请使用Collection(或适当的List或Set)代替数组。通过泛型,您可以获得在Java 5之前缺乏的类型检查。此外,仅公开接口,您可以自由更改实现(例如,将ArrayList切换为LinkedList)。
数组和泛型不太搭配。因此,如果要利用泛型,通常应避免使用数组。即:您无法通用地创建数组。例如,如果T是一种泛型类型,则“new T [0]”无法编译。您必须执行类似于“(T []) new Object [0]”之类的操作,这会生成未经检查的强制转换警告。出于同样的原因,您不能在varargs中使用泛型类型而没有警告。
使用Collections.unmodifiableCollection(以及类似的方法),您可以获得只读约束(您无法使用数组实现-您将不得不返回数组的克隆)。
您无法强制执行成员的不可变性,但是您也无法使用数组实现该操作。

谢谢!暂时+1(我会等一下其他答案)。你可以看出我还没有认真阅读JDK6 javadoc... ;) - VonC
"数组和泛型并不很搭配。因此,如果您想利用泛型的优势,通常应避免使用数组。" -> 嗯?您能解释一下这一部分吗?我的数组和泛型很搭配啊... - Nicolas
你不能通用地创建一个数组。例如,如果T是一个泛型类型,那么“new T [0]”就无法编译。你需要做类似于“(T []) new Object [0]”这样的事情,它会生成一个未经检查的转换警告。出于同样的原因,你不能在varargs中使用泛型类型而不带有警告。 - Dan Dyer

11

实际上,数组仍然比集合/列表具有一个优点。由于Java通过类型擦除实现泛型的方式,您不能拥有只有Collection的泛型类型不同但接受相同参数的两个方法。

例如:

public void doSomething(Collection<String> strs) { ... }
public void doSomething(Collection<Integer> ints) { ... }

以上两种方法无法编译,因为javac编译器使用类型擦除技术,无法将类型信息传递给JVM。JVM只会看到接受Collection作为参数的两个方法。

在这些情况下,最好的解决方法是让方法接受数组作为参数,并在将参数传递给它们时使用Collection/List的toArray()方法。如果您仍然想在上述方法中使用Collection/List,请使用java.util.Arrays.asList()方法将其转换回List。

例如:

public void doSomething(String[] strs) {
        List<String> strList = Arrays.asList(strs);
        ...
}

public void doSomething(Integer[] ints) {
        List<Integer> intList = Arrays.asList(ints);
        ...
}

public static void main(String[] args) {
        List<String> strs = new ArrayList<String>();
        List<Integer> ints = new ArrayList<Integer>();
        obj.doSomething(strs.toArray());
        obj.doSomething(ints.toArray());
}

4
请查看 Collections 类中的不可修改方法,以使返回的集合为只读/不可变。这将在列表中包装原始集合,以防止访问更改列表本身(元素本身并非不可变)。
例如,unmodifiableList
对于我来说,这取决于返回的列表将如何使用。它将被传递给哪些其他方法,以及它将如何使用?通常情况下,我更喜欢泛型集合而不是数组,但会根据具体情况进行更改。
如果您正在执行关键操作,则数组可能会更快。

此处没有实时处理。该集合不代表“属性”,只是计算出的值。 - VonC

3
我的回答被downvote了,因为我使用了proxy来代理类,并且使用这些工具(proxy和reflection)是被禁止的,因为它会影响一些人的性能(很多人甚至不使用reflection,但他们使用hibernate和spring,这些工具使用reflection,类代理和xml)。
cglib.sourceforge.net项目中的增强器(Enhancer),它允许为类创建代理(JDK仅支持接口的代理)。代理允许您控制对象中的方法,但您无法使用反射访问字段。如果您有任何问题,请问。

感谢您提供的代码和精度。+1,但我可能不会很快在我的代码中使用那种额外的层... - VonC
我理解,但我真正想说的是它就像使用Collections.unmodifiableCollection。除非你的集合不是由你修改,否则它什么也不会给你。 - Eggs McLaren

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接