Java集合就像一種容器,能夠把多個對象(其實是對象的引用,但習慣上都稱對象)「丟進」該容器中。從Java 5 增長了泛型之後,Java集合能夠記住容器中對象的數據類型,使得編碼更加簡潔、健壯。html
Java集合大體能夠分爲兩大致系,一個是Collection,另外一個是Mapjava
java.util.Collection下的接口和繼承類關係簡易結構圖:算法
java.util.Map下的接口和繼承類關係簡易結構圖:編程
其中,Java 集合框架中主要封裝的是典型的數據結構和算法,如動態數組、雙向鏈表、隊列、棧、Set、Map 等。後端
將集合框架挖掘處理,能夠分爲如下幾個部分
1) 數據結構
List
列表、Queue
隊列、Deque
雙端隊列、Set
集合、Map
映射
2) 比較器
Comparator
比較器、Comparable
排序接口
3) 算法
Collections
經常使用算法類、Arrays
靜態數組的排序、查找算法
4) 迭代器
Iterator
通用迭代器、ListIterator
針對 List
特化的迭代器數組
List集合的特色就是存取有序,能夠存儲重複的元素,能夠用下標進行元素的操做緩存
List主要實現類:ArrayList、LinkedList、Vector、Stack。安全
ArrayList是一個動態數組結構,支持隨機存取,尾部插入刪除方便,內部插入刪除效率低(由於要移動數組元素);若是內部數組容量不足則自動擴容,所以當數組很大時,效率較低。數據結構
LinkedList是一個雙向鏈表結構,在任意位置插入刪除都很方便,可是不支持隨機取值,每次都只能從一端開始遍歷,直到找到查詢的對象,而後返回;不過,它不像 ArrayList 那樣須要進行內存拷貝,所以相對來講效率較高,可是由於存在額外的前驅和後繼節點指針,所以佔用的內存比 ArrayList 多一些。多線程
Vector也是一個動態數組結構,一個元老級別的類,早在jdk1.1就引入進來類,以後在jdk1.2裏引進ArrayList,ArrayList大部分的方法和Vector比較類似,二者是不一樣的,Vector是容許同步訪問的,Vector中的操做是線程安全的,可是效率低,而ArrayList全部的操做都是異步的,執行效率高,但不安全!
關於Vector
,如今用的不多了,由於裏面的get
、set
、add
等方法都加了synchronized
,因此,執行效率會比較低,若是須要在多線程中使用,能夠採用下面語句建立ArrayList對象
List<Object> list =Collections.synchronizedList(new ArrayList<Object>());
也能夠考慮使用複製容器 java.util.concurrent.CopyOnWriteArrayList
進行操做,例如:
final CopyOnWriteArrayList<Object> cowList = new CopyOnWriteArrayList<String>(Object);
Stack是Vector的一個子類,本質也是一個動態數組結構,不一樣的是,它的數據結構是先進後出,取名叫棧!
關於Stack
,如今用的也不多,由於有個ArrayDeque
雙端隊列,能夠替代Stack
全部的功能,而且執行效率比它高!
Set集合的特色:元素不重複,存取無序,無下標;
Set主要實現類:HashSet、LinkedHashSet和TreeSet。
HashSet底層是基於 HashMap 的k
實現的,元素不可重複,特性同 HashMap。
LinkedHashSet底層也是基於 LinkedHashMap 的k
實現的,同樣元素不可重複,特性同 LinkedHashMap。
一樣的,TreeSet也是基於 TreeMap 的k
實現的,一樣元素不可重複,特性同 TreeMap;
Set集合的實現,基本都是基於Map中的鍵作文章,使用Map中鍵不能重複、無序的特性;因此,咱們只須要重點關注Map的實現便可!
Queue是一個隊列集合,隊列一般是指「先進先出」(FIFO)的容器。新元素插入(offer)到隊列的尾部,訪問元素(poll)操做會返回隊列頭部的元素。一般,隊列不容許隨機訪問隊列中的元素。
Queue主要實現類:ArrayDeque、LinkedList、PriorityQueue。
ArrayQueue是一個基於數組實現的雙端隊列,能夠想象,在隊列中存在兩個指針,一個指向頭部,一個指向尾部,所以它具備「FIFO隊列」及「棧」的方法特性。
既然是雙端隊列,那麼既能夠先進先出,也能夠先進後出,如下是測試例子!
先進先出
public static void main(String[] args) { ArrayDeque<String> queue = new ArrayDeque<>(); //入隊 queue.offer("AAA"); queue.offer("BBB"); queue.offer("CCC"); System.out.println(queue); //獲取但不出隊 System.out.println(queue.peek()); System.out.println(queue); //出隊 System.out.println(queue.poll()); System.out.println(queue); }
輸出結果:
[AAA, BBB, CCC] AAA [AAA, BBB, CCC] AAA [BBB, CCC]
先進後出
public static void main(String[] args) { ArrayDeque<String> stack = new ArrayDeque<>(); //壓棧,此時AAA在最下,CCC在最外 stack.push("AAA"); stack.push("BBB"); stack.push("CCC"); System.out.println(stack); //獲取最後添加的元素,但不刪除 System.out.println(stack.peek()); System.out.println(stack); //彈出最後添加的元素 System.out.println(stack.pop()); System.out.println(stack); }
輸出結果:
[CCC, BBB, AAA] CCC [CCC, BBB, AAA] CCC [BBB, AAA]
LinkedList是List接口的實現類,也是Deque的實現類,底層是一種雙向鏈表的數據結構,在上面我們也有所介紹,LinkedList能夠根據索引來獲取元素,增長或刪除元素的效率較高,若是查找的話須要遍歷整合集合,效率較低,LinkedList同時實現了stack、Queue、PriorityQueue的全部功能。
例子
public static void main(String[] args) { LinkedList<String> ll = new LinkedList<>(); //入隊 ll.offer("AAA"); //壓棧 ll.push("BBB"); //雙端的另外一端入隊 ll.addFirst("NNN"); ll.forEach(str -> System.out.println("遍歷中:" + str)); //獲取隊頭 System.out.println(ll.peekFirst()); //獲取隊尾 System.out.println(ll.peekLast()); //彈棧 System.out.println(ll.pop()); System.out.println(ll); //雙端的後端出列 System.out.println(ll.pollLast()); System.out.println(ll); }
輸出結果:
遍歷中:NNN 遍歷中:BBB 遍歷中:AAA NNN AAA NNN [BBB, AAA] AAA [BBB]
PriorityQueue也是一個隊列的實現類,此實現類中存儲的元素排列並非按照元素添加的順序進行排列,而是內部會按元素的大小順序進行排列,是一種可以自動排序的隊列。
例子
public static void main(String[] args) { PriorityQueue<Integer> queue1 = new PriorityQueue<>(10); System.out.println("處理前的數據"); Random rand = new Random(); for (int i = 0; i < 10; i++) { Integer num = rand.nextInt(90) + 10; System.out.print(num + ", "); queue1.offer(num); // 隨機兩位數 } System.out.println("\n處理後的數據"); for (int i = 0; i < 10; i++) { // 默認是天然排序 [升序] System.out.print(queue1.poll() + ", "); } }
輸出結果:
處理前的數據 36, 23, 24, 11, 12, 26, 79, 96, 14, 73, 處理後的數據 11, 12, 14, 23, 24, 26, 36, 73, 79, 96,
Map是一個雙列集合,其中保存的是鍵值對,鍵要求保持惟一性,值能夠重複。
Map 主要實現類:HashMap、LinkedHashMap、TreeMap、IdentityHashMap、WeakHashMap、Hashtable、Properties。
關於HashMap,相信你們都不陌生,繼承自AbstractMap,key 不可重複,由於使用的是哈希表存儲元素,因此輸入的數據與輸出的數據,順序基本不一致,另外,HashMap最多隻容許一條記錄的 key 爲 null。
HashMap 的子類,內部使用鏈表數據結構來記錄插入的順序,使得輸入的記錄順序和輸出的記錄順序是相同的。LinkedHashMap與HashMap最大的不一樣處在於,LinkedHashMap輸入的記錄和輸出的記錄順序是相同的!
可以把它保存的記錄根據鍵排序,默認是按鍵值的升序排序,也能夠指定排序的比較器,當用 Iterator 遍歷時,獲得的記錄是排過序的;如需使用排序的映射,建議使用 TreeMap。TreeMap實際使用的比較少!
繼承自AbstractMap,與HashMap有些不一樣,在獲取元素的時候,經過==
代替equals ()
來進行判斷,比較的是內存地址。
get方法源碼部分
public V get(Object key) { Object k = maskNull(key); Object[] tab = table; int len = tab.length; int i = hash(k, len); while (true) { Object item = tab[i]; //用==比較k和元素是否相等 if (item == k) return (V) tab[i + 1]; if (item == null) return null; i = nextKeyIndex(i, len); } }
WeakHashMap繼承自AbstractMap,被稱爲緩存Map,向WeakHashMap中添加元素,再次經過鍵調用方法獲取元素方法時,不必定獲取到元素值,由於WeakHashMap 中的 Entry 可能隨時被 GC 回收。
Hashtable,一個元老級的類,鍵值不能爲空,與HashMap不一樣的是,方法都加了synchronized
同步鎖,是線程安全的,可是效率上,沒有HashMap快!
同時,HashMap 是 HashTable 的輕量級實現,他們都完成了Map 接口,區別在於 HashMap 容許K和V爲空,而HashTable不容許K和V爲空,因爲非線程安全,效率上可能高於 Hashtable。
若是須要在多線程環境下使用HashMap,可使用以下的同步器來實現或者使用併發工具包中的ConcurrentHashMap
類
Map<String, Object> map =Collections.synchronizedMap(new HashMap<>());
Properties繼承自HashTable,Properties新增了load()和和store()方法,能夠直接導入或者將映射寫入文件,另外,Properties的鍵和值都是String類型。
Comparable和Comparator接口都是用來比較大小的,通常在TreeSet、TreeMap接口中使用的比較多,主要用於解決排序問題。
Comparable:對實現它的每一個類的對象進行總體排序
package java.lang; import java.util.*; public interface Comparable<T> { public int compareTo(T o); }
若一個類實現了Comparable 接口,實現 Comparable 接口的類的對象的 List 列表 ( 或數組)能夠經過 Collections.sort(或 Arrays.sort)進行排序。
此外,實現 Comparable 接口的類的對象 能夠用做 「有序映射 ( 如 TreeMap)」 中的鍵或 「有序集合 (TreeSet)」 中的元素,而不須要指定比較器。
使用例子:
/** * 實體類Person實現Comparable接口 */ public class Person implements Comparable<Person>{ private int age; private String name; public Person(String name, int age){ this.name = name; this.age = age; } @Override public int compareTo(Person o){ return this.age-o.age; } @Override public String toString(){ return name+":"+age; } }
測試
public static void main(String[] args) { Person person1 = new Person("張三",18); Person person2 = new Person("李四",17); Person person3 = new Person("王五",19); List<Person> list = new ArrayList<>(); list.add(person1); list.add(person2); list.add(person3); System.out.println(list); Collections.sort(list); System.out.println(list); }
輸出:
[張三:18, 李四:17, 王五:19] [李四:17, 張三:18, 王五:19]
Comparator:也是對實現它的每一個類的對象進行排序
package java.util; import ***; public interface Comparator<T> { int compare(T o1, T o2); ...... }
若是咱們的這個類Person
沒法修改或者沒有繼承Comparable
接口,咱們又要對其進行排序,Comparator就能夠派上用場了。
將類Person
實現的Comparable
接口去掉
/** * 實體類Person */ public class Person { private int age; private String name; public int getAge() { return age; } public void setAge(int age) { this.age = age; } public Person(String name, int age){ this.name = name; this.age = age; } @Override public String toString(){ return name+":"+age; } }
測試類:
public static void main(String[] args) { Person person1 = new Person("張三",18); Person person2 = new Person("李四",17); Person person3 = new Person("王五",19); List<Person> list = new ArrayList<>(); list.add(person1); list.add(person2); list.add(person3); System.out.println(list); Collections.sort(list, new Comparator<Person>() { @Override public int compare(Person o1, Person o2) { if(o1 == null || o2 == null){ return 0; } //o1比o2小,返回負數 //o1等於o2,等於0 //o1大於o2,返回正數 return o1.getAge()-o2.getAge(); } }); System.out.println(list); }
輸出:
[張三:18, 李四:17, 王五:19] [李四:17, 張三:18, 王五:19]
java.util.Collections工具類爲集合框架提供了不少有用的方法,這些方法都是靜態的,在編程中能夠直接調用。整個Collections工具類源碼差很少有4000行,這裏只針對一些典型的方法進行闡述。
addAll:向指定的集合c中加入特定的一些元素elements
public static <T> boolean addAll(Collection<? super T> c, T… elements)
binarySearch:利用二分法在指定的集合中查找元素
#集合元素T實現Comparable接口的方式,進行查詢 public static <T> int binarySearch(List<? extends Comparable<? super T>> list, T key) #元素之外部實現Comparator接口的方式,進行查詢 public static <T> int binarySearch(List<? extends T> list, T key, Comparator<? super T> c)
#集合元素T實現Comparable接口的方式,進行排序 public static <T extends Comparable<? super T>> void sort(List<T> list) #元素之外部實現Comparator接口的方式,進行排序 public static <T> void sort(List<T> list, Comparator<? super T> c)
shuffle:混排,隨機打亂原來的順序,它打亂在一個List中可能有的任何排列的蹤影。
#方法一 public static void shuffle(List<?> list) #方法二,指定隨機數訪問 public static void shuffle(List<?> list, Random rnd)
reverse:集合排列反轉
#直接反轉集合的元素 public static void reverse(List<?> list) #返回可使集合反轉的比較器Comparator public static <T> Comparator<T> reverseOrder() #集合的反轉的反轉,若是cmp不爲null,返回cmp的反轉的比較器,若是cmp爲null,效果等同於第二個方法. public static <T> Comparator<T> reverseOrder(Comparator<T> cmp)
synchronized系列:確保所封裝的集合線程安全(強同步)
#同步Collection接口下的實現類 public static <T> Collection<T> synchronizedCollection(Collection<T> c) #同步SortedSet接口下的實現類 public static <T> SortedSet<T> synchronizedSortedSet(SortedSet<T> s) #同步List接口下的實現類 public static <T> List<T> synchronizedList(List<T> list) #同步Map接口下的實現類 public static <K,V> Map<K,V> synchronizedMap(Map<K,V> m) #同步SortedMap接口下的實現類 public static <K,V> SortedMap<K,V> synchronizedSortedMap(SortedMap<K,V> m)
java.util.Arrays工具類也爲集合框架提供了不少有用的方法,這些方法都是靜態的,在編程中能夠直接調用。整個Arrays工具類源碼有3000多行,這裏只針對一些典型的方法進行闡述。
asList:將一個數組轉變成一個List,準確來講是ArrayList
public static <T> List<T> asList(T... a) { return new ArrayList<>(a); }
注意:這個List是定長的,企圖添加或者刪除數據都會報錯java.lang.UnsupportedOperationException
sort:對數組進行排序,適合byte,char,double,float,int,long,short等基本類型,還有Object類型
#基本數據類型,例子int類型數組 public static void sort(int[] a) #Object類型數組 #若是使用Comparable進行排序,Object須要實現Comparable #若是使用Comparator進行排序,可使用外部比較方法實現 public static void sort(Object[] a)
binarySearch:經過二分查找法對已排序的數組進行查找。若是數組沒有通過Arrays.sort排序,那麼檢索結果未知。
適合byte,char,double,float,int,long,short等基本類型,還有Object類型和泛型。
#基本數據類型,例子int類型數組,key爲要查詢的參數 public static int binarySearch(int[] a, int key) #Object類型數組,key爲要查詢的參數 #若是使用Comparable進行排序,Object須要實現Comparable #若是使用Comparator進行排序,可使用外部比較方法實現 public static int binarySearch(Object[] a, Object key)
copyOf:數組拷貝,底層採用System.arrayCopy(native方法)實現。
適合byte,char,double,float,int,long,short等基本類型,還有泛型數組。
#基本數據類型,例子int類型數組,newLength新數組長度 public static int[] copyOf(int[] original, int newLength) #T爲泛型數組,newLength新數組長度 public static <T> T[] copyOf(T[] original, int newLength)
copyOfRange:數組拷貝,指定必定的範圍,底層採用System.arrayCopy(native方法)實現。
適合byte,char,double,float,int,long,short等基本類型,還有泛型數組。
#基本數據類型,例子int類型數組,from:開始位置,to:結束位置 public static int[] copyOfRange(int[] original, int from, int to) #T爲泛型數組,from:開始位置,to:結束位置 public static <T> T[] copyOfRange(T[] original, int from, int to)
equals:判斷兩個數組的每個對應的元素是否相等(equals, 對於兩個數組的元素a和a2有a==null ? a2==null : a.equals(a2))
#基本數據類型,例子int類型數組,a爲原數組,a2爲目標數組 public static boolean equals(int[] a, int[] a2) #Object數組,a爲原數組,a2爲目標數組 public static boolean equals(Object[] a, Object[] a2)
deepEquals:主要針對一個數組中的元素仍是數組的狀況(多維數組比較)
#Object數組,a1爲原數組,a2爲目標數組 public static boolean deepEquals(Object[] a1, Object[] a2)
toString:將數組轉換成字符串,中間用逗號隔開
#基本數據類型,例子int類型數組,a爲數組 public static String toString(int[] a) #Object數組,a爲數組 public static String toString(Object[] a)
deepToString:當數組中又包含數組,就不能單純的利用Arrays.toString()了,使用此方法將數組轉換成字符串
#Object數組,a爲數組 public static String deepToString(Object[] a)
JCF的迭代器(Iterator)爲咱們提供了遍歷容器中元素的方法。只有容器自己清楚容器裏元素的組織方式,所以迭代器只能經過容器自己獲得。每一個容器都會經過內部類的形式實現本身的迭代器。
ArrayList<String> list = new ArrayList<String>(); list.add(new String("a1")); list.add(new String("a2")); list.add(new String("a3")); Iterator<String> it = list.iterator();//獲得迭代器 while(it.hasNext()){ String obj = it.next();//訪問元素 System.out.println(obj); }
JDK 1.5 引入了加強的for循環,簡化了迭代容器時的寫法
//使用加強for迭代 ArrayList<String> list = new ArrayList<String>(); list.add(new String("a1")); list.add(new String("a2")); list.add(new String("a3")); for(String obj : list){ //enhanced for statement System.out.println(obj); }
以上,主要是對java集合的總體架構進行簡單的介紹,若是有理解不當之處,歡迎指正。
一、JDK1.7&JDK1.8 源碼
二、Otokaze's Blog - Java Collection框架
三、CSDN - 朱小廝 - Comparable與Comparator淺析
做者:炸雞可樂
原文出處:www.pzblog.cn