title: Java創建List、Map等集合對象的同時進行賦值操作
date: 2019-11-28 23:25:47
tags: JavaSE
categories: JavaSE
問題簡介
在Java當中,若希望在創建數組的同時給數組賦值很簡單,可以想下面這樣:
int[] num = {1,2,3};
String strs = {"a", "b", "c"}
但是,如果我們創建List集合,或者Map集合時,也想快速地為它賦初始值,應當如何做呢?
解決方式
方式1:調用函數
請看如下代碼:
ArrayList<String> list = new ArrayList<>(Arrays.asList("aa", "bb", "cc"));
Arrays.asList(T... a) 方法的參數是一個可變長參數,也就是說他能夠傳入一個數組,也能夠傳入多個參數,而它的作用就是將傳入的數組或多個數據封裝成List集合返回,而上面的代碼就是接收返回的List集合,並將其作為參數傳入ArrayList的構造方法,創建一個新的ArrayList對象。
說到這里有人可能要問了,為什么不能直接將asList方法的返回值賦給list參數,而要將它傳入構造器創建新的對象呢?這不是脫褲子放屁——多此一舉嗎。當然不是,請看下面的代碼:
// 代碼1
List<String> list1 = Arrays.asList("aa", "bb", "cc");
list1.add("dd"); // UnsupportedOperationException
// 代碼2
String[] str = {"a","b","c"};
List<String> list = Arrays.asList(str);
str[0] = "e"; // list中的0號位置也一同改變
上面有兩段代碼,看似沒有問題,但是運行結果卻和大家想象的有些不同。首先代碼1,使用asList方法返回的創建的List對象,不允許進行修改操作,否則將會拋出一個UnsupportedOperationException;再來看代碼2,我們將一個數組作為asList的參數,得到一個List對象,但是此時我們改變這個數組中元素的值,list對象的值也會發生改變,因為這個List對象底層引用的就是這個數組,並且和代碼1一樣,這個list也不能修改。
但是,若我們將返回的List對象作為參數傳入ArrayList的構造器中,這個問題就不會發生,因為ArrayList的構造器將會把傳入的list中所有的元素復制一份,因此不會影響到原數組,且可以隨意改變。
方式2:匿名內部類
這是一個非常機智的方式,就是看到了下面這行代碼,我才忍不住寫了這篇博客:
List<String> list = new ArrayList<String>(){ {add("a"); add("b"); add("c");} };
乍一看是不是有點懵逼,我們將這段代碼展開來看,就會清晰很多:
List<String> list = new ArrayList<String>() {
{
add("a");
add("b");
add("c");
}
};
這下應該比之前容易理解了。這段代碼就是創建了一個匿名內部類對象,且這個類繼承自ArrayList,在這個匿名內部類中添加了一個非靜態代碼塊,並在代碼塊中調用了三次add方法,為這個List對象賦值。
我們知道,若我們想創建一個對象,可以直接new 構造方法,但是我們若想寫一個匿名內部類,這個匿名內部類繼承自某個類,只需在構造方法后面加上一對大括號。同時,非靜態代碼塊會在構造方法執行前被執行,所以我們將賦值語句放在了代碼塊中,於是就有了上面這段代碼。若還是看不明白,沒關系,看下面這段代碼十有八九就明白了,我們將上面的代碼換另一種方式寫出來:
public class Test {
public static void main(String[] args) {
List<String> list = new MyList();
}
}
// 創建一個類繼承自ArrayList
class MyList extends ArrayList{
// 在類的非靜態代碼塊中編寫賦值語句
{
add("a");
add("b");
add("c");
}
}
以上代碼就是最開始那句代碼的完整版,創建一個MyList類(名字隨意),繼承自ArrayList,並編寫一個非靜態代碼塊調用三次add方法,這個代碼塊將會在構造方法執行前被執行,因此創建一個MyList對象后,它肯定已經有三條數據了。若到此時還沒有聽懂,可能就需要去了解一下匿名內部類,以及代碼塊的執行機制了。
這種為集合賦值的好處就是,它可以用在任意一種集合類型上(Map,Set......),如下代碼:
// 使用此方法為map賦值
HashMap<String, Integer> map = new HashMap<String, Integer>() {
{
put("a", 1);
put("b", 2);
put("c", 3);
}
};
當然,這種方法也有一些弊端,就拿ArrayList來說,那就是這種方法得到的對象,它的類型並不是ArrayList,我們調用對象.getClass().getName()方法獲取對象的類名,得到的是代碼所在類的類名+$1(這里和匿名內部類的機制有關,就不詳細敘述了)。所以在代碼中,如果對對象的類型有着嚴格的要求,就需要謹慎考慮是否應該使用這種方式。
博客總結
在平常編寫代碼時,還是第一種方式使用的比較多,因為簡單而且不容易產生問題;而第二種方,我個人建議還是少用(雖然我就是為第二種方式寫的博客.....),因為在類型要求嚴格的程序中,可能會產生問題。當然,第二種方式真的非常機智(感嘆),而且可以用在各種類型的集合上,學習一下還是很有幫助的。
參考文獻
《Java核心技術 卷Ⅰ》