在Java中将数组转换为列表

1279

如何在Java中将数组转换为列表?

我使用了Arrays.asList(),但其行为(及签名)从Java SE 1.4.2(文档现已存档)到8发生了变化,大多数我在网上找到的代码片段都使用1.4.2的行为。

例如:

int[] numbers = new int[] { 1, 2, 3 };
Arrays.asList(numbers)
  • 在 1.4.2 版本中返回包含元素 1、2、3 的列表
  • 在 1.5.0+ 版本中返回包含数组 'numbers' 的列表

在许多情况下,这应该很容易检测到,但有时它可能会不经意地滑过:

Assert.assertTrue(Arrays.asList(numbers).indexOf(4) == -1);

25
我认为您的示例有问题:Arrays.asList(new int[] { 1, 2, 3 });在Java 1.4.2中绝对无法编译通过,因为一个 int[] 不是一个 Object[]。请注意,这里的关键问题是 int[]Object[] 之间的转换。 - Joachim Sauer
1
哦,你可能是对的。在发布之前,我没有Java 1.4.2编译器来测试我的示例。现在,在你的评论和Joe的回答之后,一切都变得更加清晰明了。 - Alexandru
1
我认为自动装箱应该已经涵盖了从原始类型到包装类 Integer 的转换。您可以先进行强制转换,然后上述 Arrays.asList 代码就可以正常工作了。 - Horse Voice
2
Java 8的Stream.boxed()会处理自动装箱并可用于此。请参见我的答案下面 - Ibrahim Arief
25个回答

1708
在您的示例中,这是因为您不能拥有原始类型的列表。换句话说,List<int> 是不可能的。
但是,您可以使用包装 int 原始类型的 Integer 类来拥有一个 List<Integer>。使用 Arrays.asList 实用方法将数组转换为 List
Integer[] numbers = new Integer[] { 1, 2, 3 };
List<Integer> list = Arrays.asList(numbers);

请在 IdeOne.com 上查看这段 代码的实时运行


128
更简洁的写法:Arrays.asList(1, 2, 3); - Kong
67
它是如何知道不要创建一个List<Integer[]>呢? - Thomas Ahle
8
它并不创建一个List<Integer[]>,而是创建一个List<Integer>对象。如果你想保证类型安全,可以这样写:Arrays.<Integer>asList(spam); - user1712376
9
@ThomasAhle 这是一个好问题。我猜这只是Java编译器中的一条规则。例如,下面的代码会返回一个List<Integer[]>: Integer[] integerArray1 = { 1 }; Integer[] integerArray2 = { 2 }; List<Integer[]> integerArrays = Arrays.asList(integerArray1, integerArray2); - Simon
2
@pedrambashiri 它不适用于原始数组,比如 int[]long[]double[],只适用于 Integer[]Long[]Double[] ... - deFreitas
显示剩余13条评论

265

在Java 8中,您可以使用流(streams):

int[] numbers = new int[] { 1, 2, 3 };
Arrays.stream(numbers)
      .boxed()
      .collect(Collectors.toList());

1
这很好,但对于boolean[]不起作用。我想那是因为制作Boolean[]同样容易。那可能是更好的解决方案。无论如何,这是一个有趣的问题。 - GlenPeterson
4
在Java 16中,可以使用Arrays.stream(spam).boxed().toList()来简化代码。这段代码的意思是将数组spam转换为一个流,然后将其每个元素装箱为包装类型,最后将结果收集到一个列表中。 - Gonen I

193

由于int是原始类型,因此我们无法使用 List<int> ,而只能使用 List<Integer>

Java 16

Java 16 在Stream API中引入了一个名为toList()的新方法。这个方便的方法返回一个不可修改的List,其中包含流元素。因此,尝试向列表中添加新元素将只会导致UnsupportedOperationException异常。

int[] ints = new int[] {1,2,3,4,5};
Arrays.stream(ints).boxed().toList();

Java 8(整型数组)

int[] ints = new int[] {1,2,3,4,5};
List<Integer> list11 =Arrays.stream(ints).boxed().collect(Collectors.toList()); 

Java 8及以下版本(整数数组)

Integer[] integers = new Integer[] {1,2,3,4,5};
List<Integer> list21 =  Arrays.asList(integers); // returns a fixed-size list backed by the specified array.
List<Integer> list22 = new ArrayList<>(Arrays.asList(integers)); // good
List<Integer> list23 = Arrays.stream(integers).collect(Collectors.toList()); //Java 8 only

需要 ArrayList 而不是 List 吗?

如果我们想要特定的 List 实现,例如 ArrayList,那么可以使用 toCollection 方法:

ArrayList<Integer> list24 = Arrays.stream(integers)
                          .collect(Collectors.toCollection(ArrayList::new));

为什么list21无法进行结构修改?

当我们使用Arrays.asList时,返回的列表大小是固定的,因为返回的列表不是java.util.ArrayList,而是在java.util.Arrays内部定义的一个私有静态类。因此,如果我们向返回的列表添加或删除元素,将会抛出UnsupportedOperationException异常。所以当我们想要修改列表时,应该选择list22。如果我们使用Java8,则还可以选择list23

明确一点,list21可以被修改,例如我们可以调用list21.set(index,element),但是不能对列表进行结构性修改,即不能添加或删除元素到列表中。你也可以参考我的这个回答来获取更多解释。


如果我们想要一个不可变的列表,那么我们可以将其封装为:

List<Integer> list22 = Collections.unmodifiableList(Arrays.asList(integers));

需要注意的另一点是,方法Collections.unmodifiableList返回指定列表的不可修改视图。不可修改视图集合是一个不可修改的集合,也是对支持集合的视图。请注意,对支持集合的更改仍然可能,并且如果发生更改,则可以通过不可修改视图看到。

在Java 9和10中,我们可以拥有真正的不可变列表。

真正的不可变列表

Java 9:

String[] objects = {"Apple", "Ball", "Cat"};
List<String> objectList = List.of(objects);

Java 10(真正的不可变列表):

我们可以使用Java 9引入的List.of,也可以使用其他方法:

  1. List.copyOf(Arrays.asList(integers))
  2. Arrays.stream(integers).collect(Collectors.toUnmodifiableList());

@BasilBourque 我的意思是不能通过添加/删除来修改列表的结构,但可以更改元素。 - akhil_mittal
实际上,我尝试了一些代码并确认使用Arrays.asList返回的列表调用List::addList::remove会失败。该方法的JavaDoc写得很差,对此点不够清晰。在第三次阅读时,我认为“固定大小”这个短语的意思是不能添加或删除元素。 - Basil Bourque
全面的答案,并应被接受。 - Mubasher

151

说到转换方式,它取决于你为什么需要你的List。 如果你只需要读取数据。好的,这里是:

Integer[] values = { 1, 3, 7 };
List<Integer> list = Arrays.asList(values);

但是如果你做这样的事情:

list.add(1);

你会得到java.lang.UnsupportedOperationException的错误。

所以在某些情况下,你甚至需要这样做:

Integer[] values = { 1, 3, 7 };
List<Integer> list = new ArrayList<Integer>(Arrays.asList(values));

第一种方法实际上并没有将数组转换,而是像List一样“表示”它。但是数组仍然具有固定数量元素等所有属性。请注意,在构建ArrayList时需要指定类型。


1
"immutability" 对我有点困惑。显然你可以改变数组的值,但是你无法改变它的大小。 - Tim Pohlmann

133

问题在于Java 5引入了varargs,不幸的是Arrays.asList()也增加了一个带有vararg参数的版本。因此,Java 5编译器会将Arrays.asList(numbers)解释为int数组的vararg参数。

这个问题在《Effective Java第2版》第7章第42条中有更详细的解释。


4
我理解发生了什么,但不知道为什么没有记录下来。我正在寻找一种替代方案,而不是重新实现相同的方法。 - Alexandru

16

最近我不得不将一个数组转换为列表。程序后来过滤了这个列表,试图删除数据。当你使用Arrays.asList(array)函数时,你创建了一个固定大小的集合:既不能添加也不能删除。这篇文章比我更好地解释了这个问题:为什么我在尝试从列表中删除元素时会收到UnsupportedOperationException异常?

最终,我不得不进行“手动”转换:

    List<ListItem> items = new ArrayList<ListItem>();
    for (ListItem item: itemsArray) {
        items.add(item);
    }

我想我可以通过使用List.addAll(items)操作将数组转换为列表。


12
new ArrayList<ListItem>(Arrays.asList(itemsArray)) 的意思是创建一个包含给定 itemsArray 中所有元素的新 ArrayList 对象,并将其作为 ListItem 类型的集合返回。 - Marco13
1
@BradyZhu:虽然上面的答案没有解决我的固定大小数组问题,但你基本上是在说RTFM,这总是不好的形式。请详细说明答案有什么问题,否则就不要发表评论了。 - Steve Gelman
2
检查工具可能会在此处显示警告,并且您已经命名了原因。无需手动复制元素,请改用Collections.addAll(items, itemsArray) - Darek Kay
刚刚遇到了这个异常。恐怕Marco13的回答应该是正确的答案。我可能需要检查整年的代码来修复所有的“asList”异常。 - user1021364

11

更短:

List<Integer> list = Arrays.asList(1, 2, 3, 4);

9
在Java 9中,您可以通过新的方便工厂方法List.of使用不可变列表来实现更加优雅的解决方案。
List<String> immutableList = List.of("one","two","three");

(shamelessly copied from here )


请注意,如果您想了解更多有关此内容的技术细节,请参阅JEP 269:集合的便利工厂方法和主要作者Stuart Marks的演讲 - Basil Bourque

9

如果您使用Apache commons-lang,则可以使用另一种解决方法:

int[] numbers = new int[] { 1, 2, 3 };
Arrays.asList(ArrayUtils.toObject(numbers));

ArrayUtils.toObjectint[] 转换为 Integer[]


9

使用数组

将一个数组转换为List的最简单方式是使用以下方法。但是,如果您尝试向列表中添加新元素或删除现有元素,将会抛出UnsupportedOperationException异常。

Integer[] existingArray = {1, 2, 3};
List<Integer> list1 = Arrays.asList(existingArray);
List<Integer> list2 = Arrays.asList(1, 2, 3);

// WARNING:
list2.add(1);     // Unsupported operation!
list2.remove(1);  // Unsupported operation!

使用ArrayList或其他List实现

您可以使用for循环将数组的所有元素添加到List实现,例如ArrayList

List<Integer> list = new ArrayList<>();
for (int i : new int[]{1, 2, 3}) {
  list.add(i);
}

在Java 8中使用Stream API

你可以将数组转换成一个流,然后使用不同的收集器(collectors)来收集这个流:Java 8默认的收集器在背后使用ArrayList,但是你也可以使用你喜欢的其他实现方式。

List<Integer> list1, list2, list3;
list1 = Stream.of(1, 2, 3).collect(Collectors.toList());
list2 = Stream.of(1, 2, 3).collect(Collectors.toCollection(ArrayList::new));
list3 = Stream.of(1, 2, 3).collect(Collectors.toCollection(LinkedList::new));

另请参阅:


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接