问题

我想为测试目的创建一个选项列表。起初,我这样做了:

ArrayList<String> places = new ArrayList<String>();
places.add("Buenos Aires");
places.add("Córdoba");
places.add("La Plata");

然后我重构了代码,如下所示:

ArrayList<String> places = new ArrayList<String>(
    Arrays.asList("Buenos Aires", "Córdoba", "La Plata"));

有一个更好的方法吗?


#1 热门回答(1692 赞)

如果你只是将它声明为List会更简单 - 它必须是一个ArrayList吗?

List<String> places = Arrays.asList("Buenos Aires", "Córdoba", "La Plata");

或者如果你只有一个元素:

List<String> places = Collections.singletonList("Buenos Aires");

这意味着places不可变(试图改变它会导致抛出UnsupportedOperationException异常)。

要创建一个可变列表,这是一个具体的ArrayList,你可以从不可变列表创建一个ArrayList

ArrayList<String> places = new ArrayList<>(Arrays.asList("Buenos Aires", "Córdoba", "La Plata"));

#2 热门回答(1599 赞)

实际上,可能“初始化”ArrayList“的最佳方式是你写的方法,因为它不需要以任何方式创建一个新的List:

ArrayList<String> list = new ArrayList<String>();
list.add("A");
list.add("B");
list.add("C");

问题是,引用list实例需要进行相当多的输入。

还有其他的选择,比如用一个实例初始化器(也称为“双括号初始化”)创建一个匿名内部类:

ArrayList<String> list = new ArrayList<String>() {{
    add("A");
    add("B");
    add("C");
}};

然而,我并不太喜欢这种方法,因为你最终得到的是一个ArrayList的子类,它有一个实例初始化器,并且该类被创建只是为了创建一个对象 - 这似乎有点矫枉过正对我来说。

如果Collection Literals proposalforProject Coin被接受(它将在Java 7中引入,但它不太可能成为Java 8的一部分),那么最好的结果是:

List<String> list = ["A", "B", "C"];

不幸的是,它不会帮助你,因为它会初始化一个不可变的List而不是ArrayList,而且它还不可用,如果它永远不会存在的话。


#3 热门回答(569 赞)

#简单的答案

在Java 8或更早版本中:

List<String> strings = Arrays.asList("foo", "bar", "baz");

这会给你一个由数组支持的List,所以它不能改变长度。
但你可以调用List.set,所以它仍然是可变的。

在Java 9中:

List<String> strings = List.of("foo", "bar", "baz");

这将为您提供一个不可变的List,因此无法更改。
在大多数情况下,您希望在预先填充它的情况下使用哪种方法。

#较短的答案

使用静态导入可以使Arrays.asList更短:

List<String> strings = asList("foo", "bar", "baz");

静态导入:

import static java.util.Arrays.asList;

任何现代IDE都会建议并自动为您做。
例如,在IntelliJ IDEA中,按“Alt Enter”并选择“静态导入方法...”。

不过,我不建议缩短Java 9List.of方法,因为只有of会变得令人困惑。
 List.of已经足够短并且读得很好。

#使用流

为什么它必须是一个List
使用Java 8或更高版本,您可以使用更灵活的“Stream”:

Stream<String> strings = Stream.of("foo", "bar", "baz");

你可以连接Streams:

Stream<String> strings = Stream.concat(Stream.of("foo", "bar"),
                                       Stream.of("baz", "qux"));

或者你可以从Stream转到List

List<String> strings = Stream.of("foo", "bar", "baz").collect(toList());

但最好只使用Stream而不将其收集到List

#如果你真的特别需要一个java.util.ArrayList

(你可能不会。)
引用JEP 269(强调我的):

使用一组预定义的值初始化可变集合实例有一小组用例。通常最好将这些预定义的值放在一个不可变的集合中,然后通过拷贝构造函数初始化可变集合。

如果你想要tobothprepopulate一个ArrayList并在之后添加(为什么?),请使用

List<String> strings = new ArrayList<>(asList("foo", "bar", "baz"));

或者在Java 9中:

List<String> strings = new ArrayList<>(List.of("foo", "bar", "baz"));

或使用Stream

List<String> strings = Stream.of("foo", "bar", "baz")
                             .collect(toCollection(ArrayList::new));

但同样,最好直接使用Stream而不是将它收集到List

#编程接口,而不是实现

你说你已经在你的代码中声明了一个ArrayList列表,但是你只应该这样做,如果你使用的不是List中的ArrayList的某个成员。

你最可能没有做的事。

通常你应该通过你将要使用的最通用的接口声明变量(例如IterableCollectionList),并用特定的实现初始化它们(例如ArrayListLinkedList或者Arrays.asList())。

否则,你会限制你的代码到那个特定的类型,并且当你想要更改时会更难。

例如:

// Iterable if you just need iteration, for (String s : strings):
Iterable<String> strings = new ArrayList<>();   

// Collection if you also need .size() or .stream():
Collection<String> strings = new ArrayList<>(); 

// List if you also need .get(index):
List<String> strings = new ArrayList<>();       

// Don't declare a specific list implementation
// unless you're sure you need it:
ArrayList<String> strings = new ArrayList<>();  // You don't need ArrayList

另一个例子是总是声明变量InputStream,即使它通常是FileInputStreamBufferedInputStream,因为有一天你或其他人想要使用其他类型的InputStream


原文链接