概念區分: java.util.Collection和java.util.Collections區別:
1、 java.util.Collection 是一個集合介面(集合類的一個頂級介面)。它提供了對集合對象進行基本操作的通用介面方法。Collection介面在Java 類庫中有很多具體的實現。Collection介面的意義是為各種具體的集合提供了最大化的統一操作方式,其直接繼承介面有List與Set。
2、java.util.Collections 是一個封裝類(工具類/協助類)。它包含有各種有關集合操作的靜態多態方法。此類不能執行個體化,就像一個工具類,用於對集合中元素進行排序、搜尋以及安全執行緒等各種操作,服務於Java的Collection架構。
既然Collections是服務於Collection架構的,那麼我們就嘗試學習使用:
方法1:
java.util.Collections.unmodifiableCollection(Collection<? extends Double>): 返回指定 collection 的不可修改視圖。
package com.daxin.collections;import java.util.ArrayList;import java.util.Collection;import java.util.Collections;public class Main1 {public static void main(String[] args) {Collection<Double> list = new ArrayList<Double>();double array[] = { 75, 52, 99, 102, 78,23 };System.out.println("list.hashCode()1 "+list.hashCode());for (int i = 0; i < array.length; i++) {list.add(array[i]);}System.out.println("list.hashCode()2 "+list.hashCode());//返回不可以修改視圖Collection<Double> unmodifiableCollection = Collections.unmodifiableCollection(list);unmodifiableCollection.add(1.2);//拋異常for (Double d : unmodifiableCollection) {System.out.println(d);}}}
當我們對返回的不可修改視圖進行調用unmodifiableCollection.add(1.2)修改時候,會拋出異常:
Exception in thread "main" java.lang.UnsupportedOperationExceptionat java.util.Collections$UnmodifiableCollection.add(Collections.java:1075)at com.daxin.collections.Main1.main(Main1.java:24)
那究竟是如何?不可以修改的呢? 答案:使用的代理模式,具體過程看源碼如下:
返回的不可修改檢視類型為:java.util.Collections.UnmodifiableCollection<E>,是Collections的一個靜態內部類,而unmodifiableCollection方法實現如下:
public static <T> Collection<T> unmodifiableCollection(Collection<? extends T> c) { return new UnmodifiableCollection<>(c); }
其中c是 UnmodifiableCollection類中的一個成員:
final Collection<? extends E> c;
此處是不是會有疑問。c是final的,但是內部成員應該可以修改。為什麼。
拓展補充:首先ArrayList的實現底層就是使用的數組:
public class ArrayList<E> extends AbstractList<E>implements List<E>, RandomAccess, Cloneable, java.io.Serializable{private static final long serialVersionUID = 8683452581122892189L;private static final int DEFAULT_CAPACITY = 10;private static final Object[] EMPTY_ELEMENTDATA = {};private transient Object[] elementData;...等}
所以對於 final Collection<? extends E> c中finalc為什麼不可修修改感覺疑問呢。其實此處使用代理模式是對add方法進行了增強,看一下add方法就知道了:
public boolean add(E e) { throw new UnsupportedOperationException(); }
答案自然見分曉了吧。
但是需要注意的是,如果我們自己在代碼中修改了原始的那個list,那麼unmodifiableCollection也會被修改,這裡的視圖類似資料庫的視圖。
接下來看看方法:
public static <K,V> Map<K,V> synchronizedMap(Map<K,V> m)
返回一個同步的Map。返回由指定映射支援的同步(安全執行緒的)映射。為了保證按順序訪問,必須通過返回的映射完成所有對底層實現映射的訪問。
首先看一下非安全執行緒的HashMap:
package com.daxin.collections;import java.util.HashMap;import java.util.Map;class Service {private Map map = null;public Service(Map map) {super();this.map = map;}public Map getMap() {return map;}public void setMap(Map map) {this.map = map;} }class ThreadA extends Thread {private Service service;public ThreadA(Service service) {super();this.service = service;}@Overridepublic void run() {for (int i = 1; i <= 5; i++) {service.getMap().put(i + "", i + "");}}}class ThreadB extends Thread {private Service service;public ThreadB(Service service) {super();this.service = service;}@Overridepublic void run() {for (int i = 1; i <= 5; i++) {service.getMap().put(i + "", i + "AAA");}}}public class Main2 {public static void main(String[] args) throws Exception {Map map=new HashMap<String,String>();Service service = new Service(map);ThreadA a = new ThreadA(service);ThreadB b = new ThreadB(service);a.start();b.start();a.join();b.join();System.out.println(service.getMap());// 輸出{3=3AAA, 2=2AAA, 1=1AAA, 5=5AAA, 4=4AAA} 或者{3=3AAA, 2=2AAA, 1=1,// 5=5AAA, 4=4AAA} 多次運行結果不一致,兩個線程交替執行導致結果多次輸出不一致。出現了安全執行緒問題//安全執行緒的請看Main3源碼}}
改進版,安全執行緒版本:
package com.daxin.collections;import java.util.Collections;import java.util.HashMap;import java.util.Map;public class Main3 {public static void main(String[] args) throws Exception {Map map=Collections.synchronizedMap(new HashMap<String,String>());Service service = new Service(map);ThreadA a = new ThreadA(service);ThreadB b = new ThreadB(service);a.start();b.start();a.join();b.join();System.out.println(service.getMap());//輸出:{3=3AAA, 2=2AAA, 1=1AAA, 5=5AAA, 4=4AAA}或者{3=3, 2=2, 1=1, 5=5, 4=4} 兩種結果了,這就主要看//誰先得到鎖了誰後得到鎖了,輸出的是最後得到鎖的結果。//不會兩個線程交替執行了}}
接下來看看synchronizedMap的方法源碼:
public static <K,V> Map<K,V> synchronizedMap(Map<K,V> m) { return new SynchronizedMap<>(m); }
再看看SynchronizedMap構造方法(SynchronizedMap是Collections的一個靜態內部類):
private static class SynchronizedMap<K, V> implements Map<K, V>, Serializable {private static final long serialVersionUID = 1978198479659022715L;private final Map<K, V> m; // Backing Mapfinal Object mutex; // Object on which to synchronize,同步加鎖對象SynchronizedMap(Map<K,V> m) { if (m==null) throw new NullPointerException(); this.m = m; mutex = this;//將mutex設定為當前對象,還有其他建構函式自行查看}...等}
再看看SynchronizedMap的put等常用方法源碼:
public V put(K key, V value) { synchronized (mutex) {return m.put(key, value);} } public V remove(Object key) { synchronized (mutex) {return m.remove(key);} } public void putAll(Map<? extends K, ? extends V> map) { synchronized (mutex) {m.putAll(map);} }每一次對SynchronizedMap進行操作其實都是對mutex對象加鎖進行的,使用的是synchronized代碼塊實現。
在看看java.util.Collections.synchronizedSet(Set<String>)方法,原理類似:
建立一個SynchronizedSet返回(SynchronizedSet繼承SynchronizedCollection類,SynchronizedCollection中實現對成員mutex的成員進行代碼塊同步)。
方法java.util.Collections.synchronizedList(List<String>)實現原理也是如上。