问题
我想为测试目的创建一个选项列表。起初,我这样做了:
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");
你可以连接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(强调我的):
使用一组预定义的值初始化可变集合实例有一小组用例。通常最好将这些预定义的值放在一个不可变的集合中,然后通过拷贝构造函数初始化可变集合。
如果你想要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
的某个成员。
你最可能没有做的事。
通常你应该通过你将要使用的最通用的接口声明变量(例如Iterable
,Collection
或List
),并用特定的实现初始化它们(例如ArrayList
,LinkedList
或者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
,即使它通常是FileInputStream
或BufferedInputStream
,因为有一天你或其他人想要使用其他类型的InputStream
。