在一行中初始化一个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"));
有一个更好的方法吗?
实际上,初始化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 Coin的Collection Literals提案被接受(它将在Java 7中引入,但它不太可能成为Java 8的一部分),那么最好的结果是:
List<String> list = ["A", "B", "C"];
不幸的是,它不会帮助你,因为它会初始化一个不可变List
而不是一个ArrayList
,而且,它现在还不可用,如果它永远会存在的话。
如果你只是把它声明为一个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"));
简单的答案
在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
s
为什么它必须是一个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", "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
,但是你只应该这样做,如果你使用ArrayList
中不在List
中的某个成员。
你最可能没有做的事。
通常你应该只使用你将要使用的最普通的接口(例如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
。
上一篇: Initialization of an ArrayList in one line
下一篇: What's the difference between tilde(~) and caret(^) in package.json?