簡體   English   中英

Arrays.asList() 沒有正常工作?

[英]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[][] (這里, Tint[] )並最終得到一個 1 元素List<int[]>這不是你想要的。

不過,您仍然有一些不錯的選擇:

Guava 的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 內部流

在 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 CollectionsIntLists 工廠直接從int值數組創建集合。 這消除了對intInteger任何裝箱的需要。

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以及SetBagStackMap

注意:我是 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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM