程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> JAVA綜合教程 >> Java基礎加強之集合篇(模塊記憶、精要分析),java精要

Java基礎加強之集合篇(模塊記憶、精要分析),java精要

編輯:JAVA綜合教程

Java基礎加強之集合篇(模塊記憶、精要分析),java精要


 

千裡之行,始於足下。把別人的變成自己,再把自己的分享給別人,這也是一次提升的過程。本文的目的是以一篇文章從整體把握集合體系又不失一些細節上的實現,高手路過。

集合的作用與特點

Java是一門面向對象語言,數據多了用對象封裝存儲(比如,人有姓名、年齡、性別等數據信息,我們就抽象一個Person對象來封裝存儲),對象多了又用什麼來存儲呢?集合,集合就是用來存儲對象的。

 

集合的特點就是適用於存儲對象而且可以存儲不同類型的對象,集合的長度是可變的。

 

集合框架圖

集合既然能存儲不同類型的的對象,那麼集合體系中肯定有不同類型的容器,集合中主要有List、Set、Map三種容器,每種容器的數據結構都不一樣。集合體系的類、接口非常多,方便記憶,我們將下面這個相對簡潔的集合框架圖分為從5個模塊來學習:

1.Collection集合

2.Map集合

3.集合遍歷

4.集合比較

5.集合工具類

 

 

先對圖做一下解析:

l 點線框表示接口

l 實現框表示具體的類

l 帶有空心箭頭的點線表示一個具體的類實現了一個接口

l 實心箭頭表示某一個類可以生成箭頭所指向的類

l 常用的容器用黑色粗線表示

 

Collection集合

Collection接口

Collection是一個接口,是高度抽象出來的集合,包含了集合的基本操作:添加、刪除、清空、遍歷、是否為空、獲取大小等等。定義如下:

public interface Collection<E> extends Iterable<E> {}

Collection接口下主要有兩個子接口:List和Set。

List接口

List是Collection的一個子接口,List中的元素是有序的,可以重復,有索引。

定義如下:

public interface List<E> extends Collection<E> {}

 

 

List是繼承於Collection接口,它自然就包含了Collection中的全部函數接口,由於List有自己的特點(素是有序的,可以重復,有索引),因此List也有自己特有的一些操作,如上紅色框框中的。List特有的操作主要是帶有索引的操作和List自身的迭代器(如上圖)。List接口的常用實現類有ArrayList和LinkedList。

 

ArrayList

ArrayList是List接口的一個具體的實現類。ArrayList的底層數據結構是數組結構,相當於一個動態數組。ArrayList的特點是隨機查詢速度快、增刪慢、線程不安全。ArrayList和Vector的區別是:Vector是線程安全的(synchronized實現)。定義如下:

public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{}

l ArrayList 繼承AbstractList,AbstractList是對一些公有操作的再次抽象,抽出一些特性操作,如根據索引操作元素,生產迭代器等。

l ArrayList 實現了RandmoAccess接口,即提供了隨機訪問功能,可以通過元素的索引快速獲取元素對象。

l ArrayList 實現了Cloneable接口,即覆蓋了函數clone(),能被克隆。

l ArrayList 實現java.io.Serializable接口,這意味著ArrayList支持序列化,能通過序列化去傳輸。

 

ArrayList源碼分析總結(源碼略,建議先整體認識再打開源碼對著看):

 

l 可以通過構造函數指定ArrayList的容量大小,若不指定則默認是10。

l 添加新元素時,容量不足以容納全部元素時,會重新創建一個新的足以容納全部元素的數組,把舊數組中的元素拷貝到新的數組中並返回新的數組。增量具體看源碼。

l 添加的新元素添加到數組末尾。

l 查找、刪除元素使用equals方法比較對象。

l ArrayList的克隆函數,即是將全部元素克隆到一個數組中。

l ArrayList的序列化,即通過java.io.ObjectOutputStream將ArrayList的長度和每一個元素寫到輸出流中,或者通過java.io.ObjectInputStream從輸入流中讀出長度和每一個元素到一個數組中。

LinkedList

LinkedList是List接口的一個具體的實現類。LinkedList底層數據結構是雙向的鏈表結構,可以當成堆棧或者隊列來使用。LinkedList的特點是增刪速度很快,順序查詢效率較高,隨機查詢稍慢。定義如下:

public class LinkedList<E>
    extends AbstractSequentialList<E>
    implements List<E>, Deque<E>, Cloneable, java.io.Serializable
{} 

l LinkedList 是繼承AbstractSequentialList,AbstractSequentialList是AbstractList的一個子類。

l LinkedList 實現 Deque 接口,即能將LinkedList當作雙端隊列使用。

l 同ArrayList,LinkedList同樣支持克隆和序列化。

 

LinkedList 源碼分析總結(源碼略,建議先整體認識再打開源碼對著看):

 

l LinkedList的本質是一個雙向鏈表,各有一個頭節點和尾節點,每一個節點都有個數據域、一個指向上一個節點的指針域、一個指向上下一個節點的指針域。

l 添加元素時,將元素添加到雙向鏈表的末端。

l 查找、刪除元素時,使用equals方法比較對象。

l 根據索引查找元素時,若索引值index < 雙向鏈表長度的1/2,則從表頭往後查找,否則從表尾往前找。

l LinkedList的克隆和序列化原理同ArrayList。

l LinkedList實現了Deque,Deque接口定義了在雙端隊列兩端訪問元素的方法,每種方法都存在兩種形式:一種是在操作失敗時拋出異常,另一種是返回一個特殊值(null 或 false)。

l LinkedList有自己特有的迭代器ListIterator。

l LinkedList不存在擴容增量的問題。

List應用舉例

LinkedList實現堆棧和隊列

LinkedList可以當成堆棧和隊列使用,因為實現了Deque接口,Deque接口提供了一些了出棧入棧和出隊入隊的方法。

class MyQueueStack { private LinkedList link; MyQueueStack() { link = new LinkedList(); } public void add(Object obj) { link.addFirst(obj); } public Object remove() { //return link.removeLast();//隊列 return link.removeFirst();//棧 } public boolean isNull() { if(!link.isEmpty()) { return false; } return true; } } public class LinkedListQueueStack { public static void main(String[] args) { MyQueueStack q = new MyQueueStack(); q.add("001"); q.add("002"); q.add("003"); while(!q.isNull()) System.out.println(q.remove()); } } 隊列運行結果: 001 002 003 棧運行結果: 003 002 001 View Code

ArrayList保證唯一性

List中的元素是可以重復的,可以通過重寫equals方法實現唯一性。下面是ArrayList實現元素去重(依據equals)的一個例子。

 

class Teacher { private String name; private String age; Teacher(String name,String age) { this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getAge() { return age; } public void setAge(String age) { this.age = age; } @Override public boolean equals(Object obj) { if(!(obj instanceof Teacher)) { return false; } Teacher p = (Teacher)obj; System.out.println("調用equals比較" + p.name + "與" + this.name); return p.name.equals(this.name) && p.age.equals(this.age); } } public class ArrayListDuplicate { public static void main(String[] args) { ArrayList al = new ArrayList(); al.add(new Teacher("zhangsan","20")); al.add(new Teacher("lingsi","21")); al.add(new Teacher("wangwu","22")); al.add(new Teacher("wangwu","22")); print("-----------原始ArrayList----------"); Iterator it = al.iterator(); while(it.hasNext()) { Teacher obj = (Teacher)it.next(); print(obj.getName() + "-" + obj.getAge()); } print("-----------刪除重復元素----------"); ArrayList al2 = removeSameObj(al); Iterator it2 = al2.iterator(); while(it2.hasNext()) { Teacher obj = (Teacher)it2.next(); print(obj.getName() + "-" + obj.getAge()); } print("-----------刪除lingsi元素----------"); al2.remove(new Teacher("lingsi","21")); Iterator it3 = al2.iterator(); while(it3.hasNext()) { Teacher obj = (Teacher)it3.next(); print(obj.getName() + "-" + obj.getAge()); } } public static ArrayList removeSameObj(ArrayList al) { ArrayList newAl = new ArrayList(); Iterator it = al.iterator(); while(it.hasNext()) { Object obj = it.next(); if(!newAl.contains(obj)) { newAl.add(obj); } } return newAl; } public static void print(Object obj) { System.out.println(obj); } } 運行結果: -----------原始ArrayList---------- zhangsan-20 lingsi-21 wangwu-22 wangwu-22 -----------刪除重復元素---------- 調用equals比較zhangsan與lingsi 調用equals比較zhangsan與wangwu 調用equals比較lingsi與wangwu 調用equals比較zhangsan與wangwu 調用equals比較lingsi與wangwu 調用equals比較wangwu與wangwu zhangsan-20 lingsi-21 wangwu-22 -----------刪除lingsi元素---------- 調用equals比較zhangsan與lingsi 調用equals比較lingsi與lingsi zhangsan-20 wangwu-22 View Code

從運行結果中可以看出,調用list的contains方法和remove方法時,調用了equals方法進行比較(List集合中判斷元素是否相等依據的是equals方法)

 

Set接口

Set是Collection的一個子接口,Set中的元素是無序的,不可以重復。定義如下:

public interface Set<E> extends Collection<E> {}

Set是繼承於Collection接口,它自然就包含了Collection中的全部函數接口。Set接口的常用實現類有HashSet和TreeSet。實際上HashSet是通過HashMap實現的,TreeSet是通過TreeMap實現的。在此只簡要列舉它們的一些特點,可以直接看下面的HashMap和TreeMap的分析,理解了HashMap和TreeMap,也就理解了HashSet和TreeSet。

HashSet

HashSet是Set接口的一個具體的實現類。其特點是元素是無序的(取出順序和存入順序不一定一樣),不可以重復。定義如下:

public class HashSet<E>
    extends AbstractSet<E>
    implements Set<E>, Cloneable, java.io.Serializable
{}

l HashSet繼承AbstractSet,AbstractSet抽象了equals()、hashCode()、removeAll()三個方法。

l HashSet實現了Cloneable,Serializable接口以便支持克隆和序列化。

l HashSet保證元素唯一性依賴equals()方法和hashCode()方法。

 

TreeSet

TreeSet是Set接口的一個具體的實現類。其特點是元素是有序的,不可以重復。定義如下:

public class TreeSet<E> extends AbstractSet<E>
    implements NavigableSet<E>, Cloneable, java.io.Serializable
{}

l TreeSet繼承AbstractSet,AbstractSet抽象了equals()、hashCode()、removeAll()三個方法。

l TreeMap實現了NavigableMap接口,支持一系列的導航方法,比如返回有序的key集合。

l TreeSet實現了Cloneable,Serializable接口以便支持克隆和序列化。

l TreeSet保證元素唯一性依賴Comparator接口和Comparable接口。

Map集合

Map接口

不同於List和Set,集合體系中的Map是“雙列集合”,存儲的是內容是鍵值對(key-value),Map集合的特點是不能包含重復的鍵,每一個鍵最多能映射一個值。Map接口抽象出了基本的增刪改查操作。定義如下:

public interface Map<K,V> {}

主要方法

/**添加**/
V put(K key, V value);
void putAll(Map<? extends K, ? extends V> m);
 
/**刪除**/
void clear();
V remove(Object key);
 
/**判斷**/
boolean isEmpty();
boolean containsKey(Object key);
boolean containsValue(Object value);
 
/**獲取**/
int size();
V get(Object key);
Collection<V> values();
Set<K> keySet();
Set<Map.Entry<K, V>> entrySet();

Map接口的主要實現類有HashMap和TreeMap。

HashMap

HashMap是Map接口的一個實現類,它存儲的內容是鍵值對(key-value)。HashMap的底層是哈希表數據結構,允許存儲null的鍵和值,同時HashMap是線程不安全的。HashMap與HashTable的區別是:HashTable不可以存儲null的鍵和值,HashTable是線程安全的(synchronized實現)。

 

哈希表的定義:給定表M,存在函數f(key),對任意給定的關鍵字值key,代入函數後若能得到包含該關鍵字的記錄在表中的地址,則稱表M為哈希(Hash)表,函數f(key)為哈希(Hash) 函數。通過把關鍵字值key映射到哈希表中的一個位置來訪問記錄,以加快查找的速度。

 

HashMap定義如下:

public class HashMap<K,V>
    extends AbstractMap<K,V>
    implements Map<K,V>, Cloneable, Serializable
{}

l HashMap繼承AbstractMap,AbstractMap抽象了Map集合的一些基本操作。

l HashMap實現了Cloneable,Serializable接口以便支持克隆和序列化。

 

HashMap源碼分析總結(源碼略,建議先整體認識再打開源碼對著看):

 

l HashMap的幾個重要成員:table, DEFAULT_INITIAL_CAPACITY ,MAXIMUM_CAPACITY ,

size, threshold, loadFactor, modCount。

(1)table是一個Entry[]數組類型,Map.Entry是一個Map接口裡的一個內部接口。而Entry實際上就是一個單向鏈表,也稱為HashMap的“鏈式存儲法”。HashMap的鍵值對就是存儲在Entry對象中(一個Entry對象包含一個key、一個value、key對應的hash和指向下一個Entry的對象)。

(2)DEFAULT_INITIAL_CAPACITY是默認的初始容量是16。

(3)MAXIMUM_CAPACITY, 最大容量(初始化容量大於這個值時將被這個值替換)。

(4)size是HashMap的大小,保存的鍵值對的數量。

(5)threshold是HashMap的阈值,用於判斷是否需要調整HashMap的容量。threshold的值="容量*加載因子",當HashMap中存儲數據的數量達到threshold時,就需要將HashMap進行rehash 操作(即重建內部數據結構)是容量*2。

(6)loadFactor就是加載因子。

(7)modCount是記錄HashMap被修改的次數。

 

l get(Object key)。先計算key的hash,根據hash計算索引,根據索引在table數組中查找出對應的Entry,返回Entry中的value。

l put(K key, V value)。若key為null,則創建一個key為null的Entry對象並存儲到table[0]中。否則計算其hash以及索引i,創建一個鍵值對和哈希值分別為key、value、hash的Entry對象並存儲到table[i]中並將Entry對象添加到鏈表中。需要注意的是,如果該hash已經存在Entry鏈表中,則用新的value取代舊的value並返回舊的value。

l  putAll(Map<? extends K, ? extends V> m)。當當前實際容量小於需要的容量,則將容量*2。調用put()方法逐個添加到HashMap中。

l remove(Object key)。計算key的hash以及索引i,根據索引在table數組中查找出對應的Entry,從Entry鏈表刪除對應的Entry節點,期間會比較hash和key(equals判斷)來判斷是否是要刪除的節點。

l clear()。把每一個table[i]設置為null。

l Entry類中重寫了equals()方法和hashCode()來判斷兩個Entry是否相等。

l containsKey(Object key)。比較hash和key(equals判斷)來判斷是否是包含該key。

 

HashMap舉例

通過keyset遍歷

public class HashMapTest { public static void main(String[] args) { HashMap<String,String> map = new HashMap<String,String>(); map.put("001", "zhangsan1"); map.put("002", "zhangsan2"); map.put("003", "zhangsan3"); map.put("004", "zhangsan4"); Set<String> keySet = map.keySet(); Iterator<String> it = keySet.iterator(); while(it.hasNext()) { String obj = (String)it.next(); System.out.println(obj + "-" + map.get(obj)); } } } 運行結果: 004-zhangsan4 001-zhangsan1 002-zhangsan2 003-zhangsan3 View Code

通過entrySet遍歷

public class HashMapTest { public static void main(String[] args) { HashMap<String,String> map = new HashMap<String,String>(); map.put("001", "zhangsan1"); map.put("002", "zhangsan2"); map.put("003", "zhangsan3"); map.put("004", "zhangsan4"); Set<Entry<String, String>> se = map.entrySet(); Iterator<Entry<String, String>> it = se.iterator(); while(it.hasNext()) { Entry<String, String> en = (Entry<String, String>)it.next(); System.out.println(en.getKey() + "-" + en.getValue()); } } } 運行結果: 004-zhangsan4 001-zhangsan1 002-zhangsan2 003-zhangsan3 View Code

TreeMap

TreeMap是Map接口的一個實現類,因此它存儲的也是鍵值對(key-value)。TreeMap的底層數據結構是二叉樹(紅黑樹),其特點是可以對元素進行排序,TreeMap是線程不安全的。定義如下:

public class TreeMap<K,V>
    extends AbstractMap<K,V>
    implements NavigableMap<K,V>, Cloneable, java.io.Serializable
{}

l TreeMap繼承AbstractMap,AbstractMap抽象了Map集合的一些基本操作。

l TreeMap實現了NavigableMap接口,支持一系列的導航方法,比如返回有序的key集合。

l TreeMap實現了Cloneable,Serializable接口以便支持克隆和序列化。

 

TreeMap源碼分析總結(源碼略,建議先整體認識再打開源碼對著看):

 

l TreeMap的幾個重要成員:root, size, comparator。

(1)root是紅黑樹的根節點,它是Entry類型,Entry是紅黑數的節點,它包含了紅黑數的6個基本組成成分:key(鍵)、value(值)、left(左孩子)、right(右孩子)、parent(父節點)、color(顏色)。Entry節點根據key進行排序,Entry節點包含的內容為value。

(2)comparator是比較器,使紅黑樹的節點具有比較性,用來給TreeMap排序。紅黑數排序時,根據Entry中的key進行排序,Entry中的key比較大小是根據比較器comparator來進行判斷的。

(3)size是紅黑樹節點的個數。

 

l TreeMap(Comparator<? super K> comparator)。帶比較器的構造函數可以在TreeMap外部自定義TreeMap元素(紅黑樹節點)的比較器。

l get(Object key)。若比較器comparator不為空(即通過構造函數外部傳入的),則通過Comparator接口的compare()方法從根節點開始和key逐個比較直到找出相等的節點。若比較器為空,則使用Comparable接口的compareTo()比較查找。

l put(K key, V value)。若根節點root為空,則根據key-value創建一個Entry對象插入到根節點。否則在紅黑樹中找到找到(key, value)的插入位置。查找插入位置還是若比較器comparator不為空則通過Comparator接口的compare()方法比較查找,否則通過Comparable接口的compareTo()方法比較查找。過程比較復雜。

l putAll(Map<? extends K, ? extends V> map)。調用AbstractMap中的putAll(),AbstractMap中的putAll()又會調用到TreeMap的put()。

l remove(Object key)。先根據key查找出對應的Entry,從紅黑樹中刪除對應的Entry節點。

l clear()。clear把根節點設置為null即可。

l containsKey(Object key)。判斷是否是包含該key的元素,查找方法同get()。

 

TreeMap舉例

public class TreeMapTest { public static void main(String[] args) { System.out.println("---TreeMap默認按key升序排序---"); TreeMap<Integer,String> map = new TreeMap<Integer,String>(); map.put(200,"sha"); map.put(300,"can"); map.put(100,"pek"); map.put(400,"szx"); Set<Entry<Integer,String>> se = map.entrySet(); Iterator<Entry<Integer,String>> it = se.iterator(); while(it.hasNext()) { Entry<Integer,String> en = (Entry<Integer,String>)it.next(); System.out.println(en.getKey() + "-" + en.getValue()); } System.out.println("---TreeMap使用自定義比較器降序排序---"); TreeMap<Integer,String> map2 = new TreeMap<Integer,String>(new CityNumComparator()); map2.put(200,"sha"); map2.put(300,"can"); map2.put(100,"pek"); map2.put(400,"szx"); Set<Entry<Integer,String>> se2 = map2.entrySet(); Iterator<Entry<Integer,String>> it2 = se2.iterator(); while(it2.hasNext()) { Entry<Integer,String> en = (Entry<Integer,String>)it2.next(); System.out.println(en.getKey() + "-" + en.getValue()); } } } class CityNumComparator implements Comparator { @Override public int compare(Object o1, Object o2) { if(!(o1 instanceof Integer) || !(o2 instanceof Integer)) { throw new RuntimeException("不可比較的對象"); } Integer num1 = (Integer)o1; Integer num2 = (Integer)o2; if(num1 < num2) { return 1; } if(num1 > num2) { return -1; } return 0; //Integer類型自身已經自有比較性,或者 //return num2.compareTo(num1); } } 運行結果: ---TreeMap默認按key升序排序--- 100-pek 200-sha 300-can 400-szx ---TreeMap使用自定義比較器降序排序--- 400-szx 300-can 200-sha 100-pek View Code

由此可見,要想按照自己的方式對TreeMap進行排序,可以自定義比較器重寫compare()方法重寫自己的比較邏輯。

集合遍歷

迭代器的設計原理

在上面List和Map的分析和舉例中,已經提到迭代器了。因為每一種容器的底層數據接口不同,所以迭代的方式也不同,但是也有共同的部分,把這些共同的部分再次抽象,Iterator接口就是迭代器的頂層抽象。

 

List集合中有自己特有的迭代器ListIterator,ListIterator接口是Iterator接口的子接口。實現類ArrayList和LinkedList中有迭代器的具體實現(內部類)。把迭代器設計在集合的內部,這樣,迭代器就可以輕松的訪問集合的成員了。

private class ListItr extends Itr implements ListIterator<E>{}

而在HashMap和TreeMap的遍歷中,我們經常將Map的鍵存到Set集合中,Set屬於List的子接口,當然也繼承了List特有的迭代器,通過迭代key來獲取value。另一種方式是將Map中的映射關系Entry取出存儲到Set集合,通過迭代每一個映射Entry關系獲取Entry中的key和value。可以說,Map是借List的迭代器實現了對自身的遍歷。

集合遍歷效率總結

l ArrayList是數組結構,ArrayList常用遍歷有for循環(索引訪問)、增強for循環、迭代器迭代。其中按索引訪問效率最高,其次是增強for循環,使用迭代器的效率最低。

l LinkedList是鏈表結構,常用遍歷也有for循環(索引訪問)、增強for循環、迭代器迭代。建議不要采用隨機訪問的方式去遍歷LinkedList,而采用逐個遍歷的方式。

l HashMap和TreeMap的遍歷中,轉換為set,借List的迭代器實現對自身的遍歷。

集合比較

Comparator與Comparable

在TreeMap的分析和舉例中,我們提到了TreeMap中的元素排序比較采用的是Comparator接口的compare()方法和Comparable接口的compareTo()。當運算本身不具備比較性或者具備的比較性不滿足我們的需求時,我們就可以使用Comparator接口或者Comparable接口來重寫我們需要的比較邏輯。Comparator接口和Comparable接口實現比較的主要區別是:Comparator 是在元素部實現的排序,這是一種策略模式,就是不改變元素自身,而用一個策略對象(自定義比較器)來改變比較行為。而Comparable接口則需要修改要比較的元素,讓元素具有比較性,在元素內部重新修改比較行為。看下面的兩個列子。

讓元素具有比較性

//實現Comparable接口強制讓Passenger對象具有可比性 class Passenger implements Comparable { private String name; private int age; Passenger(String name,int age) { this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public int compareTo(Object obj) { if(!(obj instanceof Passenger)) { return -1; } Passenger p = (Passenger)obj; System.out.println("調用compareTo比較"+ p.name + "與" + this.name); if(this.age> p.age) { return 1; } if(this.age == p.age) { return this.name.compareTo(p.name); } return -1; } } public class TreeSetTest { public static void main(String[] args) { TreeSet ts = new TreeSet(); print("-----------添加元素到TreeSet----------"); ts.add(new Passenger("zhangsan",20)); ts.add(new Passenger("lingsi",21)); ts.add(new Passenger("wangwu",22)); ts.add(new Passenger("wangwu",22)); Iterator it = ts.iterator(); while(it.hasNext()) { Passenger obj = (Passenger)it.next(); print(obj.getName() + "-" + obj.getAge()); } } public static void print(Object obj) { System.out.println(obj); } } 運行結果: -----------添加元素到TreeSet---------- 調用compareTo比較zhangsan與zhangsan 調用compareTo比較zhangsan與lingsi 調用compareTo比較zhangsan與wangwu 調用compareTo比較lingsi與wangwu 調用compareTo比較lingsi與wangwu 調用compareTo比較wangwu與wangwu zhangsan-20 lingsi-21 wangwu-22 View Code

 

上面的例子中,Passenger本身並不具備比較性,但是通過修改Passenger,讓其實現了Comparable 接口重寫了compareTo()方法讓其根據年齡具有比較性。

自定義比較器

public class TreeSetTest2 { public static void main(String[] args) { TreeSet ts = new TreeSet(new MyComparator()); print("-----------添加元素到TreeSet----------"); ts.add(new Passenger("zhangsan",20)); ts.add(new Passenger("lingsi",21)); ts.add(new Passenger("wangwu",22)); ts.add(new Passenger("chenliu",23)); Iterator it = ts.iterator(); while(it.hasNext()) { Passenger obj = (Passenger)it.next(); print(obj.getName() + "-" + obj.getAge()); } } public static void print(Object obj) { System.out.println(obj); } } class MyComparator implements Comparator { @Override public int compare(Object o1, Object o2) { if(!(o1 instanceof Passenger) || !(o2 instanceof Passenger)) { throw new RuntimeException("不可比較的對象"); } Passenger p1 = (Passenger)o1; Passenger p2 = (Passenger)o2; System.out.println("調用compare比較"+ p1.getName() + "與" + p2.getName()); if(p1.getAge() > p2.getAge()) { return 1; } if(p1.getAge() < p2.getAge()) { return -1; } return 0; } } 運行結果: -----------添加元素到TreeSet---------- 調用compare比較zhangsan與zhangsan 調用compare比較lingsi與zhangsan 調用compare比較wangwu與zhangsan 調用compare比較wangwu與lingsi 調用compare比較chenliu與lingsi 調用compare比較chenliu與wangwu zhangsan-20 lingsi-21 wangwu-22 View Code

從上面的例子可以看出,Passenger本身並不具備比較性,我們也沒有修改Passenger,而是在外部自定義一個比較器,傳入TreeSet中,使得TreeSet中存儲的Passenger具有了比較性。

集合工具類

Collections

Collections是一個工具類,提供了操作集合的常用方法:

 

<!--排序, 對list中元素按升序進行排序,list中的所有元素都必須實現 Comparable 接口-->
void sort(List<T> list)

<!--混排,打亂list中元素的順序-->
void shuffle(List<?> list)

<!--反轉,反轉list中元素的順序-->
void reverse(List<?> list)

<!--使用指定元素替換指定列表中的所有元素-->
void fill(List<? super T> list, T obj)

<!-- 將源list中的元素拷貝到目標list-->
void copy(List<? super T> dest, List<? extends T> src)

<!-- 返回集合中最小的元素-->
T min(Collection<? extends T> coll)

<!-- 返回集合中最大的元素-->
T max(Collection<? extends T> coll)

Collections舉例

public class CollectionsTest { public static void main(String[] args) { ArrayList al = new ArrayList(); al.add("zhangsan"); al.add("lisi"); al.add("wangwu"); System.out.println("集合未排序前"); System.out.println(al); System.out.println("使用Collections.sort()方法自然排序"); Collections.sort(al); System.out.println(al); System.out.println("使用Comparator按長度自定義排序"); Collections.sort(al,new StringLengthComparator()); System.out.println(al); System.out.println("al中最長的元素"); System.out.println(Collections.max(al,new StringLengthComparator())); System.out.println("已排序的前提下,wangwu元素下標索引"); System.out.println(Collections.binarySearch(al, "wangwu")); System.out.println("逆序"); Collections.reverse(al); System.out.println(al); } } class StringLengthComparator implements Comparator<String> { @Override public int compare(String o1, String o2) { if(o1.length() > o2.length()) return 1; if(o1.length() < o2.length()) return -1; return 0; } } 運行結果: 集合未排序前 [zhangsan, lisi, wangwu] 使用Collections.sort()方法自然排序 [lisi, wangwu, zhangsan] 使用Comparator按長度自定義排序 [lisi, wangwu, zhangsan] al中最長的元素 zhangsan 已排序的前提下,wangwu元素下標索引 1 逆序 [zhangsan, wangwu, lisi] View Code

Arrays

Arrays也是一個工具類,提供了操作數組以及在數組和集合之間轉換的一些常用方法。

/**可以對各種類型的數組進行排序**/
void sort(Object[] a)
 
/** 數組變集合**/
<T> List<T> asList(T... a)
 
/** 數組轉成字符串**/
String toString(Object[] a)

Arrays舉例

public class ArraysTest { public static void main(String[] args) { String[] array = new String[]{"abc","def","hig"}; System.out.println(Arrays.toString(array)); List<String> list = Arrays.asList(array); System.out.println(list.contains("def")); //list.add("lmn");//UnsupportedOperationException int[] array2 = new int[]{1,2,3}; List<int[]> list2 = Arrays.asList(array2); System.out.println(list2); Integer[] array3 = new Integer[]{1,2,3}; List<Integer> list3 = Arrays.asList(array3); System.out.println(list3); } } 運行結果: [abc, def, hig] true [[I@36db4bcf] [1, 2, 3] View Code

需要注意的是,將數組變成集合後,不能使用集合的增加或者刪除方法,因此數組的長度是固定的,使用這些方法將會報UnsupportedOperationException異常。從上面的例子也可以看出,如果數組中的元素都是對象,那麼轉換成集合時,數組中的元素就直接轉換成集合中的元素;如果數組中的元素是基本類型,那麼整個數組將作為集合的元素存在。

小結

其實只要理解了集合體系各個主要實現類底層的數據結構類型,根據數據結構的特性,就能聯想到該集合有什麼特點,也很容易理解它們主要方法上的實現過程。文章主要介紹了常用的接口和實現類,還有很多沒提到,通過集合框架圖分模塊記憶,整體把握,再根據需要擴展其它沒提到的內容。

 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved