协慌网

登录 贡献 社区

在一行中初始化 ArrayList

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

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"));

有一个更好的方法吗?

答案

如果你只是将它声明为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"));

实际上,初始化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一个子类,它有一个实例初始化器,并且创建该类只是为了创建一个对象 - 这对我来说似乎有点过头了。

如果Project CoinCollection Literals 提议被接受(它将在 Java 7 中引入,但它也不可能成为 Java 8 的一部分),那本应该不错的是。):

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

不幸的是,它不会帮助你,因为它将初始化一个不可变的List而不是一个ArrayList ,而且,它还没有,如果有的话。

简单的答案

在 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并选择Static import method...


但是,我不建议缩短了 Java 9 List.of方法,因为有刚of变得扑朔迷离。
List.of已经足够短并且读得很好。


使用Stream

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

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

你可以连接Stream s:

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 (强调我的):

还有用于初始化可变集合实例与一组预定义值的一部分使用情况。通常最好将这些预定义值放在不可变集合中,然后通过复制构造函数初始化可变集合。


如果你想预填充的ArrayList加入到它之后(为什么?),使用

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

或者在 Java 9 中:

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

或使用Stream

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

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


编程到接口,而不是实现

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

你很可能不会这样做。

通常,您应该通过您将要使用的最通用接口(例如IterableCollectionList )声明变量,并使用特定实现(例如ArrayListLinkedListArrays.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即使它通常是一个FileInputStream或一个BufferedInputStream ,因为有一天你或其他人想要使用其他类型的InputStream