[英]Arrays.asList() not working as it should?
我有一個 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 方法接受一個可變參數,據我所知,它是數組的“簡寫”。
問題:
為什么第二段代碼返回List<int[]>
而不是List<int>
。
有沒有辦法糾正它?
為什么自動裝箱在這里不起作用; 即int[]
到Integer[]
?
Java 中沒有List<int>
這樣的東西 - 泛型不支持原語。
自動裝箱僅發生在單個元素上,而不發生在基元數組上。
至於如何糾正它 - 有各種各樣的庫提供了大量的方法來做這樣的事情。 沒有辦法解決這個問題,我認為在 JDK 中沒有任何東西可以使它更容易。 有些人會在包裝類型的列表包裝基本數組(讓拳擊發生在接入),其他人將通過原始數組迭代,因為他們去建立一個獨立的副本,裝箱。 確保你知道你在使用哪個。
(編輯:我一直假設int[]
的起點是不可協商的。如果您可以從Integer[]
開始,那么您就大功告成了 :)
僅舉一個幫助程序庫的示例,並稍微插入Guava ,有com.google.common.primitive.Ints.asList
。
這個怎么樣?
Integer[] ints = new Integer[] {1,2,3,4,5};
List<Integer> list = Arrays.asList(ints);
因為 java 數組是對象,而Arrays.asList()
將您的 int 數組視為 varargs 列表中的單個參數。
進入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
。
問題不Arrays.asList()
。 問題是您希望自動裝箱在數組上工作 - 而事實並非如此。 在第一種情況下,編譯器在查看它們的用途之前自動裝箱單個整數。 在第二種情況下,您首先將它們放入一個 int 數組(不需要自動裝箱),然后將其傳遞給Arrays.asList()
(不能自動裝箱)。
為什么自動裝箱在這里不起作用; 即 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 - ..... 前提是您不使用==
來測試自動裝箱的值是否相等。
Arrays.asList(T... a)
有效地采用T[]
,它將匹配任何真實對象( Object
子類)的數組作為數組。 唯一不匹配的是原始數組,因為原始類型不是從Object
派生的。 所以int[]
不是Object[]
。
然后發生的是 varags 機制啟動並將其視為您傳遞了單個對象,並創建該類型的單個元素數組。 所以你傳遞一個int[][]
(這里, T
是int[]
)並最終得到一個 1 元素List<int[]>
這不是你想要的。
不過,您仍然有一些不錯的選擇:
Int.asList(int[])
適配器如果您的項目已經使用了 guava,那么就像使用 Guava 提供的適配器一樣簡單: Int.asList() 。 相關類中的每個基本類型都有一個類似的適配器,例如, Booleans
for 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 中,您可以使用Arrays.stream(int[])
方法將int
數組轉換為Stream
。 根據您的用例,您可以直接使用流,例如,使用forEach(IntConsumer)
對每個元素執行某些forEach(IntConsumer)
。 在這種情況下,此解決方案非常快,並且根本不會引起任何裝箱或拆箱,並且不會創建底層數組的任何副本。
或者,如果你真的需要一個List<Integer>
,你可以使用stream.boxed().collect(Collectors.toList())
作為建議here 。 這種方法的缺點是它將列表中的每個元素完全裝箱,這可能會將其內存占用增加近一個數量級,它會創建一個新的Object[]
來保存所有裝箱的元素。 如果您隨后大量使用該列表並且需要Integer
對象而不是int
s,這可能會有所回報,但這是需要注意的。
如果您將int[]
傳遞給Arrays.asList()
,則創建的列表將是List<int[]>
,這在 Java 中無效,而不是正確的List<Integer>
。
我認為您希望Arrays.asList()
自動裝箱您的整數,正如您所見,它不會。
不可能將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;
}
或者,您可以使用IntList
作為類型,並使用Eclipse Collections的IntLists
工廠直接從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 的提交者。
int
是一種原始類型。 Arrays.asList() 接受泛型類型 T,它僅適用於引用類型(對象類型),而不適用於基元。 由於 int[] 作為一個整體是一個對象,它可以作為單個元素添加。
從 Java 9 開始有一個更好的解決方案:
List<Integer> list = List.of(null, 1, 1, 2, 4, 8, 1, 1, 2, 4, 8, 4, 8);
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.