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

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<String> list = N.asList("Buenos Aires", "Córdoba", "La Plata");
// HashSet
Set<String> set = N.asSet("Buenos Aires", "Córdoba", "La Plata");
// HashMap
Map<String, Integer> map = N.asMap("Buenos Aires", 1, "Córdoba", 2, "La Plata", 3);

// Or for Immutable List/Set/Map
ImmutableList.of("Buenos Aires", "Córdoba", "La Plata");
ImmutableSet.of("Buenos Aires", "Córdoba", "La Plata");
ImmutableSet.of("Buenos Aires", 1, "Córdoba", 2, "La Plata", 3);

// The most efficient way, which is similar with Arrays.asList(...) in JDK. 
// but returns a flexible-size list backed by the specified array.
List<String> set = Array.asList("Buenos Aires", "Córdoba", "La Plata");

声明:我是算盘通用的开发者。

其他回答

对我来说,Arrays.asList()是最好且方便的方法。我总是喜欢这样初始化。如果您是Java集合的初学者,那么我希望您参考ArrayList初始化

简单的答案

Java 9或更高版本:

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

这将为您提供一个不可变的列表,因此无法更改。在大多数情况下,这就是你想要的。

Java 8或更早版本:

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

这将为您提供一个由数组支持的List*,因此它不能更改长度。但是您可以调用List.set(…),所以它仍然是可变的。

*实现细节:它是java.util.Arrays内部的一个私有嵌套类,名为ArrayList,这是一个与java.util.ArrayList不同的类,尽管它们的简单名称相同。

静态导入

您可以通过静态导入使Java 8 Arrays.asList更短:

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

任何现代IDE*都会为您建议并执行此操作。

我不建议静态地导入List.of方法,因为它会令人困惑。

*例如,在IntelliJ IDEA中,按Alt+Enter并选择静态导入方法。。。

使用流

为什么必须是列表?对于Java 8或更高版本,您可以使用更灵活的Stream:

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

您可以连接流:

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

或者您可以从“流”转到“列表”:

import static java.util.stream.Collectors.toList;
...
var strings = Stream.of("foo", "bar", "baz").toList(); // Java 16

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

但最好只使用流而不将其收集到列表中。

如果您特别需要java.util.ArrayList*

如果要预先填充ArrayList并在之后添加到其中,请使用

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

或Java 8或更早版本:

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

或使用流:

import static java.util.stream.Collectors.toCollection;

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

但同样,最好直接使用流,而不是将其收集到列表中。

*您可能不需要特别的ArrayList。引用JEP 269:

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

(强调矿井)

程序到接口,而不是实现

您说过您已经在代码中将列表声明为ArrayList,但只有当您使用的ArrayList的某个成员不在list中时,才应该这样做。

这是你最可能不做的。

通常,您应该只通过要使用的最通用的接口(例如Iterable、Collection或List)声明变量,并使用特定的实现(例如ArrayList、LinkedList或Arrays.asList())初始化它们。

否则,您将代码限制为特定类型,并且在需要更改时将更难更改。

例如,如果要将ArrayList传递给void方法(…):

// Iterable if you just need iteration, for (String s : strings):
void method(Iterable<String> strings) { 
    for (String s : strings) { ... } 
}

// Collection if you also need .size(), .isEmpty(), or .stream():
void method(Collection<String> strings) {
    if (!strings.isEmpty()) { strings.stream()... }
}

// List if you also need random access, .get(index):
void method(List<String> strings) {
    strings.get(...)
}

// Don't declare a specific list implementation
// unless you're sure you need it:
void method(ArrayList<String> strings) {
    ??? // You don't want to limit yourself to just ArrayList
}

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

是的,借助数组,您可以在一行中初始化数组列表,

List<String> strlist= Arrays.asList("aaa", "bbb", "ccc");

正如汤姆所说:

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

但是,既然你抱怨想要一个ArrayList,你应该首先知道ArrayList是List的一个子类,你可以简单地添加这一行:

ArrayList<String> myPlaces = new ArrayList(places);

不过,这可能会让你抱怨“表现”。

在这种情况下,这对我来说没有意义,为什么,因为您的列表是预定义的,所以它没有被定义为数组(因为在初始化时大小是已知的)。如果这是你的选择:

String[] places = {"Buenos Aires", "Córdoba", "La Plata"};

如果您不关心细微的性能差异,那么您也可以非常简单地将数组复制到ArrayList:

ArrayList<String> myPlaces = new ArrayList(Arrays.asList(places));

好吧,但未来你需要的不仅仅是地名,还需要国家代码。假设这仍然是一个预定义的列表,在运行时不会更改,那么使用枚举集是合适的,如果将来需要更改列表,则需要重新编译。

enum Places {BUENOS_AIRES, CORDOBA, LA_PLATA}

将变成:

enum Places {
    BUENOS_AIRES("Buenos Aires",123),
    CORDOBA("Córdoba",456),
    LA_PLATA("La Plata",789);

    String name;
    int code;
    Places(String name, int code) {
      this.name=name;
      this.code=code;
    }
}

枚举有一个静态值方法,该方法返回一个数组,该数组按声明顺序包含枚举的所有值,例如:

for (Places p:Places.values()) {
    System.out.printf("The place %s has code %d%n",
                  p.name, p.code);
}

在这种情况下,我想你不需要你的ArrayList。

P.S.Randyaa演示了使用静态实用程序方法Collections.addAll的另一种好方法。

import com.google.common.collect.ImmutableList;

....

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