67

我有一个 float[],我想获得一个包含相同元素的列表。我可以做一件丑陋的事情,将它们一一添加,但我想使用 Arrays.asList 方法。不过有一个问题。这有效:

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

但事实并非如此。

int[] ints = new int[] {1,2,3,4,5};
List<Integer> list = Arrays.asList(ints);

asList 方法接受一个可变参数参数,据我所知,该参数是数组的“速记”。

问题:

  • 为什么第二段代码返回 aList<int[]>而不是List<int>.

  • 有没有办法纠正它?

  • 为什么自动装箱在这里不起作用;即int[]Integer[]

4

11 回答 11

91

Java 中没有 a 这样的东西List<int>——泛型不支持原语。

自动装箱只发生在单个元素上,而不是基元数组

至于如何纠正它 - 有各种各样的图书馆有大量的方法来做这样的事情。没有办法解决这个问题,而且我认为在 JDK 中没有什么可以让它变得更容易。有些将原始数组包装在包装器类型的列表中(以便在访问时发生装箱),其他人将遍历原始数组以创建一个独立的副本,并在进行时装箱。确保你知道你正在使用哪个。

(编辑:我一直假设 an 的起点int[]是不可协商的。如果你可以从 an 开始,Integer[]那么你就很远了:)

仅举一个帮助程序库的示例,并稍微插入Guavacom.google.common.primitive.Ints.asList ,有.

于 2009-09-23T18:53:10.260 回答
34

这个怎么样?

Integer[] ints = new Integer[] {1,2,3,4,5};
List<Integer> list = Arrays.asList(ints);
于 2009-09-23T18:52:15.433 回答
18

因为 java 数组是对象,并且Arrays.asList()将您的 int 数组视为 varargs 列表中的单个参数。

于 2009-09-23T18:51:46.883 回答
16

进入 Java 8,您可以执行以下操作以收集到一个盒装数组中:

Integer[] boxedInts = IntStream.of(ints).boxed().toArray(Integer[]::new);

或者这个收集在一个装箱的列表中

List<Integer> boxedInts = IntStream.of(ints).boxed().collect(Collectors.toList());

但是,这仅适用于int[]long[]double[]。这不适用于byte[].

注意Arrays.stream(ints)IntStream.of(ints)是等价的。所以前面两个例子也可以改写为:

Integer[] boxedIntArray = Arrays.stream(ints).boxed().toArray(Integer[]::new);
List<Integer> boxedIntList = Arrays.stream(ints).boxed().collect(Collectors.toList());

最后一种形式可能会受到青睐,因为它省略了Stream. 然而,在内部它仍然是一堆重载的,在这种情况下仍然在IntStream内部创建一个。

于 2015-06-04T06:16:37.000 回答
5

问题不在于Arrays.asList(). 问题是您希望自动装箱可以在数组上工作 - 但事实并非如此。在第一种情况下,编译器在查看它们的用途之前对各个 int 进行自动装箱。在第二种情况下,您首先将它们放入一个 int 数组(不需要自动装箱),然后将其传递给Arrays.asList()(不可能自动装箱)。

于 2009-09-23T18:57:38.537 回答
4

为什么自动装箱在这里不起作用;即 int[] 到 Integer[]?

虽然自动装箱会将 an 转换intInteger,但不会将 an 转换int[]Integer[]

为什么不?

简单(但不令人满意)的答案是因为这就是 JLS 所说的。(如果您愿意,可以检查它。)

真正的答案是自动装箱正在做什么以及为什么它是安全的基础。

当您1在代码中的任何位置自动装箱时,您会得到相同的Integer对象。并非所有int值都如此(由于Integer自动装箱缓存的大小有限),但如果您用于equals比较Integer对象,您会得到“正确”的答案。

基本上N == N永远是真的,new Integer(N).equals(new Integer(N))永远是真的。此外,这两件事仍然是正确的……假设您坚持使用纯 Java 代码。

现在考虑一下:

int[] x = new int[]{1};
int[] y = new int[]{1};

这些是平等的吗?不! x == y是假的,x.equals(y)是假的!但为什么?因为:

y[0] = 2;

换句话说,具有相同类型、大小和内容的两个数组总是可以区分的,因为 Java 数组是可变的。

自动装箱的“承诺”是可以这样做,因为结果无法区分1。但是,因为equals对于数组和数组可变性的定义,所有数组从根本上都是可区分的。因此,如果允许对原始类型数组进行自动装箱则会破坏“承诺”。


1 - ..... 前提是您不==用于测试自动装箱值是否相等。

于 2018-07-22T02:01:38.193 回答
3

Arrays.asList(T... a)有效地T[]将匹配任何真实对象数组(的子类Object)的 a 作为数组。唯一不匹配的是基元数组,因为基元类型不是从Object. 所以 anint[]不是Object[].

然后发生的事情是 varags 机制启动并将其视为您传递了单个对象,并创建了该类型的单个元素数组。因此,您传递了一个int[][](此处T为 is int[])并最终得到一个 1 元素List<int[]>,这不是您想要的。

不过,您仍然有一些不错的选择:

番石榴的Int.asList(int[])适配器

如果您的项目已经使用了番石榴,那么它就像使用 Guava 提供的适配器一样简单: Int.asList()。关联类中的每个原始类型都有一个类似的适配器,例如Booleansforboolean等。

int foo[] = {1,2,3,4,5};
Iterable<Integer> fooBar = Ints.asList(foo);
for(Integer i : fooBar) {
    System.out.println(i);
}

这种方法的优点是它在现有数组周围创建了一个瘦包装器,因此包装器的创建是常数时间(不依赖于数组的大小),并且所需的存储量只是一个很小的常数(小于 100 字节)以及底层整数数组。

缺点是访问每个元素需要对底层进行装箱操作int,设置需要拆箱。如果您频繁访问列表,这可能会导致大量的临时内存分配。如果您平均多次访问每个对象,则最好使用将对象装箱一次并将它们存储为Integer. 下面的解决方案就是这样做的。

Java 8 内部流

在 Java 8 中,您可以使用该Arrays.stream(int[])方法将int数组转换为Stream. 根据您的用例,您可以直接使用流,例如,对每个元素使用forEach(IntConsumer). 在这种情况下,此解决方案非常快,根本不会产生任何装箱或拆箱,并且不会创建底层数组的任何副本。

或者,如果你真的需要一个List<Integer>,你可以stream.boxed().collect(Collectors.toList())按照这里的建议使用。这种方法的缺点是它完全装箱了列表中的每个元素,这可能会增加它的内存占用近一个数量级,它会创建一个新Object[]的来保存所有装箱的元素。如果您随后大量使用列表并需要Integer对象而不是ints,这可能会得到回报,但需要注意这一点。

于 2016-06-24T00:15:15.983 回答
2

无法转换int[]Integer[],您必须复制值


int[] tab = new int[]{1, 2, 3, 4, 5};
List<Integer> list = ArraysHelper.asList(tab);

public static List<Integer> asList(int[] a) {
    List<Integer> list = new ArrayList<Integer>();
    for (int i = 0; i < a.length && list.add(a[i]); i++);
    return list;
}
于 2010-07-27T04:24:39.777 回答
2

如果您传递int[]to Arrays.asList(),则创建的列表将是List<int[]>,这在 java 中无效,而不是正确的List<Integer>

我认为您期望Arrays.asList()自动装箱您的整数,如您所见,它不会。

于 2009-09-23T18:55:36.813 回答
0

int是原始类型。Arrays.asList() 接受泛型类型 T,它仅适用于引用类型(对象类型),不适用于原语。由于 int[] 作为一个整体是一个对象,因此可以将其添加为单个元素。

于 2021-02-12T04:55:39.803 回答
0

或者,您可以使用Eclipse CollectionsIntList中的类型和IntLists 工厂来直接从值数组创建集合。这消除了对 to 的任何装箱的需要。intintInteger

IntList intList1 = IntLists.mutable.with(1,2,3,4,5);
int[] ints = new int[] {1,2,3,4,5};
IntList intList2 = IntLists.mutable.with(ints);
Assert.assertEquals(intList1, intList2);

Eclipse Collections 支持可变和不可变原语List以及SetBag和.StackMap

注意:我是 Eclipse Collections 的提交者。

于 2019-11-03T00:43:49.250 回答