https://baike.baidu.com/item/java%E9%9B%86%E5%90%88%E7%B1%BB/4758922?fr=aladdin
https://www.cnblogs.com/zhuoqingsen/p/8573643.html
集合類存放於java.util包中。
我們為什么要設定不同的集合類型,是為了放置不同的數據,而且不同類型用在不同的場合。這三個類放在何處呢,它們放在java.util包中,Set、List和Map都是接口,它們有各自的實現類。Set的主要實現類:HashSet和TreeSet,List的主要實現類是ArrayList,LinkedList而Map主要實現類是HashMap和TreeMap。
List 接口及其實現類
- 有序集合,集合中每個元素都有其對應的順序索引,類似數組,索引也是從 0 開始,可以根據元素的索引,來訪問元素。
- List 集合允許添加相同的元素,因為它是通過下標來取值的,不會因為元素相同而產生沖突。
如何應用接口List的一個重要實現類 ArrayList呢?
- ArrayList集合類的定義與添加集合元素(不同類型的元素)
2.訪問集合元素通過get(集合下標) 獲取的是抽象Object對象類型,這里需要強轉弱,自動拆箱
- String s1=(String)ls.get(0);
- int s2=(Integer)ls.get(1);
- double s1=(Double)ls.get(2);
3.刪除元素
ls.remove(1);//刪除下標為1的對象
4.遍歷集合元素
數組遍歷是通過游標遍歷,List集合類遍歷集合元素類似於數組遍歷,也可以通過索引遍歷,另外List集合類還可以使用Iterator接口遍歷集合元素(實際上,所有Collection集合類都可以使用Iterator接口遍歷集合元素,因為實現類都實現Iterable<E>接口
- 使用Iterator接口遍歷集合元素
5.對元素集合進行排序
一般有兩種方式
第一種是
封裝類實現了Comparator接口,封裝類就是平常的VO/PO類,例如User
public class User implements Comparable<User> { private String name; private Integer age; public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getAger() { return age; } public void setAge(Integer age) { this.age= age; } public int compareTo(User arg0) { return this.getAge().compareTo(arg0.getAge()); } }
//測試客戶端代碼
public static void main(String[] args) { User user1 = new User(); user1.setName("zhangsan"); user1.setAge(10); User user2 = new User(); user2.setName("lisi"); user2.setAge(6); List<User> list = new ArrayList<User>(); list.add(user1); list.add(user2); Collections.sort(list); //排序 for(User u : list){ System.out.println(u.getName()); //輸出
}
}
lisi
zhangshan
第二種是封裝類沒有實現Comparable接口,此時需要在調用sort的另一個有參方法,依舊是這個封裝類,但是沒有實現Comparable,我這里就不寫封裝類,同上
User user1 = new User(); user1.setName("zhangsan"); user1.setAge(10); User user2 = new User(); user2.setName("lisi"); user2.setAge(6); List<User> list = new ArrayList<User>(); list.add(user1); list.add(user2); //初始化list Collections.sort(list,new new Comparator<User >(){ //覆蓋排序方法 public int compare(User u1, User u2) { //按照User的年齡進行升序排列 if(u1.getAge() > u2.getAge()){ return 1; } if(u1.getAge() == u2.getAge()){ return 0; } return -1; } }); //排序
如何應用Vector?
Vector與ArrayList在用法上幾乎完全相同。但是它是線程安全的,也就是加了同步鎖,相比之下性能會顯得慢一些
那么在多線程下的ArrayList怎么樣?
兩個線程同時訪問,對容器的同一位置進行了同一時刻的賦值,則會達不到希望容器產生的效果,比如
- 循環10000次在多線程可能會小於100000次
- 在ArrayList擴容的時候會造成數組越界,產生非常詭異的異常
Set接口及其實現類
Set集合類特點是什么?
- 無序集合
- 不允許包含重復元素,根據equals方法判斷兩個對象相同
如何應用Set接口的一個重要實現類HashSet呢
1.HashSet集合類的定義與添加集合元素
- HashSet集合添加一個元素時,會先得到該對象的hashCode值,然后通過equals方法判斷集合內是否已存在相同的對象,如果已存在相同的對象,即hashcode指向的內存地址相同,則無法添加該對象。String字符串重寫了equals方法,雖然str1(小明)與str2(小張)是兩個不同的對象,但str1.equals(str2);返回是false,因此添加str2失敗。
- HashSet是先比較元素的HashCode的值,也就是哈希值,如果哈希值相同的情況下,會調用equals方法比較兩個元素的值是否相同。
- Set的另外一個實現類TreeSet則是通過調用CompareTo的方法,如果返回值為0,證明兩個元素相同,因此TreeSet其實是一個有序的集合。
2.遍歷集合元素
- HashSet集合只能通過Iterator接口或者增強for循環遍歷集合元素,且因為HashSet不能保證元素的排列順序,因而遍歷讀取集合元素的順序是有可能發生變化的
//增強for的方式
for(Person p:set){ System.out.println(p.name+p.age); }
3.刪除元素
- set.remove(p1);
Map接口及其實現類
Map集合的特點是什么?
- 雙列集合,保存兩組值,一組是鍵key,另一組是值value,key和value之間存在單向一對一關系,通過指定的key,總能找到唯一的、確定的value
-
Map集合類的key不允許重復,根據equals方法判斷兩個key相同
-
Map集合類的key和value可以是任何類型的數據(鍵和值都可以為null)
如何應用HashMap?
- HashMap集合類添加鍵值對時,先根據equals方法判斷要添加鍵值對的key是否存在,如果存在,value會發生覆蓋,如果不存在,添加新的鍵值對。
遍歷集合元素
1.keySet()方法獲取元素
原理:
- 將Map集合中的所有鍵(Key)存入到Set集合中,因為Set集合具備迭代器,所以可以用迭代方式取出所有的鍵,再根據get方法獲取每一個鍵對應的值。簡單說就是:Map集合---->Set集合 ---->迭代器取出
2.entrySet()方法獲取元素:
原理:
- 將Map集合中的映射關系(Key-Value)存入到了Set集合中,而這個映射關系的數據類型是Map.Entry,在通過迭代器將映射關系存入到Map.Entry集合中,並通過其中的getKey()和getValue()放取出鍵值。
其余還有其他遍歷方式,比如說增強for,但是前提都是要獲得Set存儲Key的集合,即調用map.KeySet(),然后轉化為String,在通過getValue("Key")的方式獲取值,在實際開發當中比如獲取數據庫查詢回來的ResultSet 都是rs,getString("字段名)
- 遍歷得到結果的順序並不是添加順序,實際上,Map里的key和Set集合的存儲形式(無序)類似,與HashSet集合不能保證元素的順序一樣,HashMap也不能保證其中key-value鍵值對的順序。
刪除集合元素
map.remove(key);
集合類之間的幾種比較(小結)
1.Vector、ArrayList、LinkedList的異同?分別適用於何種需求?
ArrayList和Vector都是采用數組方式存儲數據,此數組元素數大於實際存儲的數據以便增加和插入元素,都允許通過順序索引查找元素,但是插入數據、刪除數據要設計到數組元素移動等內存操作,所以查找數據快、增刪數據慢。ArrayList和Vector在用法上幾乎相同,但有一點明顯區別:ArrayList是線程不安全的,多線程情況下,需要寫代碼保證集合的同步性;Vector是線程安全的,它使用了synchronized方法保證了多線程情況下集合的同步性;也正因為Vector的線程安全,它在性能上比ArrayList差。
LinkedList,首先它是一個List集合,可以根據索引來隨機訪問集合中的元素,這個與ArrayList的用法相同。除此之外,LinkedList采用雙向鏈表實現存儲,查找數據時需要進行向前或向后遍歷,直到找到索引數據;但是插入數據時只需要記錄本項的前后項即可,所以查找數據慢、增刪數據快。
Arraylist 底層使用的是Object數組;LinkedList 底層使用的是雙向鏈表數據結構(注意雙向鏈表和雙向循環鏈表的區別)
ArrayList的空 間浪費主要體現在在list列表的結尾會預留一定的容量空間,而LinkedList的空間花費則體現在它的每一個元素都需要消耗比ArrayList更多的空間(因為要存放直接后繼和直接前驅以及數據)
適用需求建議:
- 需要經常遍歷集合元素,建議使用ArrayList、Vector
- 需要經常執行插入、刪除操作來改變集合的大小,建議使用LinkedList
- 多線程的情況下,可以考慮Vector。但也可以考慮ArrayList,另外寫代碼保證集合的同步性
- 關於CopyOnWriteArrayList也是一個線程安全的容器,原理是
CopyOnWriteArrayList底層就是數組,加鎖就交由ReentrantLock來完成。
在添加的時候就上鎖,並復制一個新數組,增加操作在新數組上完成,將array指向到新數組中,最后解鎖。
在修改時,復制出一個新數組,修改的操作在新數組中完成,最后將新數組交由array變量指向。
寫加鎖,讀不加鎖
2.HashSet與TreeSet的異同?分別適用於何種需求?
HashSet與TreeSet相同點:都是Set接口的實現類,集合元素都不允許重復;
HashSet與TreeSet不同點:
HashSet中的數據是無序的,可以放入null,且只能放入一個null,通過計算元素的hashCode值快速操作,所以HashSet的效率很高。
TreeSet中的數據是自動排序的,這個有序不同於List接口那樣按照插入的先后順序而且有對應的索引,這個順序是按照元素對象自己定義的排序規則而定的。默認情況下,TreeSet會調用集合元素的compareTo(Object obj)方法來比較元素之間大小關系,然后將集合元素按升序排列,基於TreeMap。因此,TreeSet中的每個對象的類都必須實現Comparable接口定義排序規則才可以正常使用,如String等這些類本身已經實現Comparable接口並有默認順序,而null沒有實現Comparable接口,所以TreeSet不允許放入null值。
3.HashMap、TreeMap、HashTable的異同?分別適用於何種需求?
HashMap和HashTable的關系類似於ArrayList和Vector的關系,HashTable與HashMap的用法基本相同,兩者的區別有:
HashMap允許有空鍵(null)與空值(null),非線程安全,效率較高;
Hashtable不允許有空鍵(null)與空值(null),線程安全,效率較低;
TreeMap與HashMap的關系也類似與TreeSet和HashSet的關系,TreeMap實現SortMap接口,能夠把它保存的鍵值對根據key排序,基於紅黑樹,從而保證TreeMap中所有鍵值對處於有序狀態。TreeMap要求所有的key必須實現Comparable接口,默認遵循key的默認排序規則,也可以指定排序的比較器。當用Iterator 遍歷TreeMap時,得到的記錄是排過序的(類似TreeSet),不允許有空鍵(null)。
by the way
fail-fast 機制是java集合(Collection)中的一種錯誤機制。當多個線程對同一個集合的內容進行操作時,就可能會產生fail-fast事件。拋java.util.ConcurrentModificationException