我想创建一个用于测试目的的选项列表。起初,我这样做了:
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 Coin的Collection 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
某个成员,你应该这样做。
你很可能不会这样做。
通常,您应该通过您将要使用的最通用接口(例如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
。