Arrays.asList() 不能正常工作?
Posted
技术标签:
【中文标题】Arrays.asList() 不能正常工作?【英文标题】:Arrays.asList() not working as it should? 【发布时间】:2010-11-30 20:59:24 【问题描述】:我有一个 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 方法接受一个 varargs 参数,据我所知,该参数是数组的“速记”。
问题:
为什么第二段代码返回List<int[]>
而不是List<int>
。
有办法改正吗?
为什么自动装箱在这里不起作用;即int[]
到Integer[]
?
【问题讨论】:
顺便说一句:float[]
数组发生了什么?
【参考方案1】:
在 Java 中没有 List<int>
这样的东西 - 泛型不支持原语。
自动装箱只发生在单个元素上,而不是 数组 的基元。
至于如何纠正它 - 有各种各样的库有大量的方法来做这样的事情。没有办法解决这个问题,而且我认为在 JDK 中没有任何东西可以让它变得更容易。有些会在包装类型列表中包装一个原始数组(以便在访问时发生装箱),其他人将遍历原始数组以创建一个独立的副本,并在进行时装箱。确保你知道你在使用哪个。
(编辑:我一直假设int[]
的起点是不可协商的。如果你可以从Integer[]
开始,那么你就很远了:)
仅举一个帮助程序库的示例,并插入Guava 一点,有com.google.common.primitive.Ints.asList
。
【讨论】:
为什么“自动装箱只发生在单个元素,而不是基元数组。”。您认为做出这一设计决定的原因是什么? @Geek:那么您会建议什么替代方案?通过复制所有元素将double[]
自动转换为Double[]
是很奇怪的——这样引用类型的转换将有效地克隆数据,这与Java 中的所有其他操作不同。如果您建议Double[]
应该能够以“视图”的方式得到double[]
的支持,那还有其他问题,例如如何存储空引用以及此数组与其他数组之间的区别.我认为这是正确的决定。
使用 java 8 你可以使用流 api: ListInts.asList
返回的列表不支持add()
或相关方法【参考方案2】:
这个怎么样?
Integer[] ints = new Integer[] 1,2,3,4,5;
List<Integer> list = Arrays.asList(ints);
【讨论】:
打败我,但最好也有解释。 难以置信...它适用于参考,但不适用于原始类型:D 非常感谢。 :) 嗯,但是有没有一种简单的方法可以将 int[] 转换为 Integer[]?问题是我通过方法调用获得了我的数组,但我无法更改它。 @Savvas:请参阅***.com/questions/880581/java-convert-int-to-integer 或 Jon Skeet 的答案。 Apache Commons Lang 或 Guava 等库会有所帮助。 可以克服警告 类型安全:类型 List 的表达式需要未经检查的转换以符合 List因为 java 数组是对象,Arrays.asList()
将您的 int 数组视为可变参数列表中的 单个 参数。
【讨论】:
@mmyers 事实上,这就是这个问题的全部内容。 +1 为 ChssPly76【参考方案4】:进入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
。
【讨论】:
您需要从toArray()
转换结果,因为它返回Object[]
不.. docs.oracle.com/javase/8/docs/api/java/util/stream/…
好的。你说的对。此时输入的是Stream<Integer>
,而不是IntStream
。但是,我已经通过使用 toArray(Integer[]::new)
来修复它。也许在这里简单地进行类型转换会更好?
这实际上是一个不同的 重载toArray()
实现(docs.oracle.com/javase/8/docs/api/java/util/stream/…),它使用了一个生成器函数 并正确地完成了工作。最好尽量避免类型转换..【参考方案5】:
问题不在于Arrays.asList()
。问题是您希望自动装箱可以在数组上工作 - 但事实并非如此。在第一种情况下,编译器在查看它们的用途之前对各个 int 进行自动装箱。在第二种情况下,您首先将它们放入一个 int 数组(不需要自动装箱),然后将其传递给 Arrays.asList()
(不能自动装箱)。
【讨论】:
【参考方案6】:为什么自动装箱在这里不起作用;即 int[] 到 Integer[]?
虽然自动装箱会将int
转换为Integer
,但它不会将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 - ..... 前提是您不使用 ==
来测试自动装箱值是否相等。
【讨论】:
【参考方案7】:Arrays.asList(T... a)
有效地将T[]
作为一个数组匹配任何真实对象数组(Object
的子类)。唯一不匹配的是基元数组,因为基元类型不是从Object
派生的。所以int[]
不是Object[]
。
然后发生的事情是 varags 机制启动并将其视为您传递了单个对象,并创建了该类型的单个元素数组。因此,您传递了一个 int[][]
(这里,T
是 int[]
)并最终得到一个 1 元素 List<int[]>
,这不是您想要的。
不过,您仍然有一些不错的选择:
Guava 的 Int.asList(int[])
适配器
如果您的项目已经使用了 guava,那么只需使用 Guava 提供的适配器即可:Int.asList()。关联类中的每个原始类型都有一个类似的适配器,例如,Booleans
用于 boolean
等。
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())
作为suggested here。这种方法的缺点是它将列表中的每个元素完全装箱,这可能会增加其内存占用近一个数量级,它会创建一个新的Object[]
来保存所有装箱的元素。如果您随后大量使用该列表并需要 Integer
对象而不是 int
s,这可能会有所回报,但需要注意。
【讨论】:
【参考方案8】:如果您将int[]
传递给Arrays.asList()
,则创建的列表将是List<int[]>
,这在java 中无效,而不是正确的List<Integer>
。
我认为您希望 Arrays.asList()
自动装箱您的整数,正如您所见,它不会。
【讨论】:
【参考方案9】:无法将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;
【讨论】:
【参考方案10】:或者,您可以使用 IntList
作为类型,并使用来自 Eclipse Collections 的 IntLists
factory 直接从 int
值的数组创建集合。这消除了对int
到Integer
的任何装箱的需要。
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
以及Set
、Bag
、Stack
和Map
。
注意:我是 Eclipse Collections 的提交者。
【讨论】:
【参考方案11】:int
是原始类型。 Arrays.asList() 接受泛型类型 T,它仅适用于引用类型(对象类型),不适用于原语。由于 int[] 作为一个整体是一个对象,它可以作为单个元素添加。
【讨论】:
以上是关于Arrays.asList() 不能正常工作?的主要内容,如果未能解决你的问题,请参考以下文章
为什么Java里的Arrays.asList不能用add和remove方法?
Arrays.asList 为什么不能 add 或者 remove 而 ArrayList 可以