之前一直沒有弄清楚過java的各種集合的差異,所以在這里合在一起對比下。
Collection and Map
java的所有集合都來自于Collection和Map這兩個接口中的其中一個。
Collection接口是普通的集合接口。
Map接口有點特殊,他的元素都是以Key-Value的形式存儲。
先對比Collection下的集合,它的下面又有兩個大類,一個list接口,一個set接口,他們最大的不同就是set中不允許有重復的元素。
List接口
List繼承了Collection接口,他是一個有序的集合,實現這個接口的類有4個:ArrayList,LinkedList,Stack和Vector。
ArrayList:我習慣把他當作一個長度可以自動增長的動態數組,通過名叫elementData的數組來保存元素,他不是線程安全的。如果不在初始化指定大小,默認為10個。關于每次的自增大小,我看見一些博客上說是:新的容量=“(原始容量x3)/2 + 1”但是我在eclipse中打斷點發現卻是:新的容量=“(原始容量x3)/2”,取整。在下面代碼中,11行打斷點。
1 public class test { 2 3 public static void main(String[] args){ 4 5 List<Integer> list = new ArrayList<Integer>(3); 6 7 for (int i = 0; i < 3; i++) { 8 list.add(i); 9 }10 11 for (int i = 0; i < 1; i++) {12 list.add(i);13 }14 15 } 16 }可以看到此時elementData長度為3,當我向下執行再向list中添加一條數據時,

可以看到,他的elementData長度為4,而不是5;我試了下初始容量為9,超出后也為13,而不是14;

LinkedList:他以鏈表的形式存儲,而且是雙向鏈表,可以get()隨機訪問,可以當作棧,隊列,雙端隊列使用,因為他實現了pop,push,peek,peekFrist,peekLast,poll,pollFrist,pollLast方法,poll返回頭,并移除。peek至返回。他不是線程安全的。
1 public class test { 2 3 public static void main(String[] args){ 4 5 LinkedList<Integer> list = new LinkedList<Integer>(); 6 7 for (int i = 0; i < 10; i++) { 8 list.push(i); 9 }10 11 System.out.PRintln(list.size());//1012 list.poll();13 System.out.println(list.size());//914 list.peek();15 System.out.println(list.size());//916 17 } 18 }ArrayList和LinkedList的選用:對于數組來說,增加一個元素,特別是在中間增加一個元素,需要移動其他元素,而鏈表不存在這種問題,鏈表只需改變指針地址即可。但是對隨機訪問,鏈表卻存在一個問題,就是鏈表的數據結構決定了他只能順序訪問,即便他可以用get()隨機訪問,也是一個一個找下來的。因此,在選擇用哪個時,應該考慮當前集合是否頻繁改變大小size,是否頻繁的隨機訪問。
Vector:感覺和ArrayList差不多,但是他是線程安全的。
Stack:標準的棧(FILO),繼承自Vector,同樣用數組實現,peek方法只取不彈。
集合的線程安全性
首先,Vector和Stack是線程安全的,而ArrayList和LinkedList不是線程安全的。我用代碼的運行結果來證明下,想法是這樣的,我用兩個線程,每個線程都向list中添加100個數,那么,按照正常的想法,最后list的大小list.size()是應該等于200的。
1 public class test { 2 3 //List<Integer> list = Collections.synchronizedList(new ArrayList<Integer>()); 4 //List<Integer> list = Collections.synchronizedList(new LinkedList<Integer>()); 5 //List<Integer> list = new ArrayList<Integer>(); 6 //List<Integer> list = new Vector<Integer>(); 7 List<Integer> list = new LinkedList<Integer>(); 8 //Stack<Integer> list = new Stack<Integer>(); 9 public static void main(String[] args) {10 test t = new test();11 12 ThreadA tA = t.new ThreadA();13 ThreadB tB = t.new ThreadB();14 //開始線程15 tA.start();16 tB.start();17 try {18 tA.join();19 tB.join();20 } catch (InterruptedException e) {21 e.printStackTrace();22 }23 System.out.println("list size:" + t.list.size());24 }25 class ThreadA extends Thread {26 @Override27 public void run() {28 super.run();29 for (int i = 0; i < 100; i++) {30 list.add(i);31 //list.push(i);32 }33 }34 }35 class ThreadB extends Thread {36 @Override37 public void run() {38 super.run();39 for (int i = 0; i < 100; i++) {40 list.add(i);41 //list.push(i);42 }43 }44 }45 }list的類型為ArrayList時的結果,不管多少次基本沒有等于200的

list的類型為Vector時的結果,不管多少次,都等于200.

ArrayList為什么會出現這種情況?
不管怎么執行,ArrayList的大小不會超過200。向ArrayList添加數據時有兩步,首先加入到數組中,然后size++,假設添加第一條數據,此時size=0,ThreadA理所當然應該在0位置添加一個值,但是此時ThreadA被阻塞,ThreadB被執行,由于ThreadA還沒來得及執行size++,所以size仍然等于0,那ThreadB也應該在0位置添加一個值,把ThreadA賦的值覆蓋掉了,ThreadB繼續執行size++,size=1。接著ThreadA繼續執行,size++,size=2,;但現實中,我的list只有一個值,size等于1。因此,可以推斷出來,ArrayList的大小不會超過200,是因為在某些時刻,ThreadA和ThreadB同時把數據寫到了一個位置上,造成了數據的丟失。
怎么解決?
就像上面代碼的第三行,在初始化時用Collections.synchronizedList()函數,即可讓ArrayList和LinkedList變成線程安全的集合。
這里強烈推薦一個博客,把集合介紹得很詳細:http://m.survivalescaperooms.com/skywang12345/p/3308513.html
新聞熱點
疑難解答