把Java陣列轉換為List時的注意事項

TP_funny發表於2015-02-11
不幸的是並不是每件事都盡如人意。舉個例子,現在將一個Java陣列轉換為List。當然,我們可以使用Arrays.toList方法,但是如果沒有慎重思考就隨便使用幾乎肯定會產生令人討厭的意外。考慮完下面這段程式並預測其輸出你就明白我的意思了:
package com.wordpress.mlangc.arrays;

import java.util.Arrays;

public class ArraysToList
{
    public static void main(final String[] args)
    {
        System.out.println(
                Arrays.asList(new String[] { "a", "b" }));

        System.out.println(
                Arrays.asList(new Integer[] { 1, 2 }));

        System.out.println(
                Arrays.asList(new int[] { 1, 2 }));

        System.out.println(
                Arrays.asList(new String[] { "a", "b" }, "c"));
    }
}


由於Javadoc對Arrays.asList的說明相當模糊,對你來說預測出程式的執行結果可能有點困難,下面我們來一步步的揭曉答案:
  • 第9行就像我們根據API所預測的那樣在我們的控制檯輸出了“
  • [a,b]”,這正是我們樂意看到的。
  • 第12行也一樣如預期那樣輸出了“
  • [1,2]”。
  • 第15行就不同了,當然這裡不是說15與12的不同,而是一個是int另一個是Integer,因此在我們的控制檯列印出了類似這樣的結果“
  • [[I@39172e08]”,這就不再如預期那樣了。我們得到一個包含陣列中標識每個元素唯一性的地址串的list,而不是包含兩個Integer物件的list。
  • 看到上面的結果後,對於第18行輸出的類似“
  • [[Ljava.lang.String;@20cf2c80, c]”這樣的結果就不會感到驚奇了。


但是發生了什麼呢?前兩個列印語句與我們預期的結果相同,因Java語言規範規定了呼叫一個宣告為foo(T… t)的方法,比如foo(new T[]{bar,baz})等同於foo(bar,baz)這樣的呼叫。在Arrays.asList方法中T是引數型別,因此它必須為一個Object 型別,但是int不是,而int[]卻是。這就是為什麼第16行的宣告等同於 Arrays.asList(new Object[] { new int[] { 1, 2 } })。
Arrays.asList(new Object[] { new int[] { 1, 2 } })


最後也是非常重要的一點,在第19行的宣告從一開始就產生了呼叫問題。我們告訴編譯器我們需要一個包含String陣列和字串的list,正如我們預期的那樣我們得到了我們想要的東西。

到現在為止解釋了這麼多,但是我們還可以從中學到更多的東西:問題的真正來源並不是可變引數設計的很糟糕;相反的我認為這個設計很好。關於這個問題在Effective Java2第 42項規範中已經解釋地很清楚了,Arrays.asList違反了該項規範,事實上Arrays.asList作為一個反面教材,告訴了我們在使用Java的可變引數設計API時為什麼要非常小心。在這裡我不會再重複那篇文章裡的回答,但是你自己確實需要親自去讀一下它,但是考慮到完整性我必須指出 上面有問題的宣告在使用Java1.4的編譯器下編譯的時候就會報錯,這是相當好的。現在我們仍然會使用Arrays.asList,但是為了安全要求我 們知道所面臨的問題的複雜性。下面是在將陣列轉換為lists的時候我們需要遵循的規則,做到這些可以確保沒有任何意外的情況發生:
  • 如果你要將一個陣列轉換為list時最好將其轉換為一個string,使用Arrays.toString代替上面的方法吧。即使對於基本型別的陣列該方法也不會出現任何問題。
  • 如果你打算將一個基本型別的陣列轉換為所對應的封裝型別的list,使用Apache Commons Lang吧,關於這個可能你很早就在專案中使用過了,類似下面這樣使用ArrayUtils.toObject:
List<Integer> list = Arrays.asList(ArrayUtils.toObject(new int[] { 1, 2 }));

請注意:一般情況下推薦使用原始資料型別陣列而不是裝箱後的原始資料型別列表。
  • 如果你打算將一個引用型別的陣列轉換為list,可以直接使用Arrays.asList:
List<String> list = Arrays.asList(new String[] { "a", "b" });


不要忘了告訴和你一起工作的人以確保他們不和你犯同樣的錯誤。當然,你也可以選擇僅僅記住那些使用Arrays.asList方法時可能出現問題的地方,並使用普通的for迴圈來代替,但是那會使你的程式碼很雜亂,還會帶來效能方面的問題。
來自:碼農網
相關閱讀
評論(1)

相關文章