重學java---5---集合
Iterator
有三個方法:
- boolean hasNext();
- E next();
- void remove();
- 很多具體集合類都提供了自己的迭代器,通過內部類來實現
- 使用迭代器進行刪除時的正確方法是
- iterator.next();
- iterator.remove();
- 呼叫remove之間沒有呼叫next是不合法的
list
常用的有 ArrayList 和 LinkedList。
LinkedList在頻繁的中間插入操作場景中比ArrayList有更好的效能。並且不用擴容。
ArrayList在兩方面的效能比LinkedList要好:
- 隨機訪問與遍歷
- 一直從尾部新增元素
Arrays.asList(T…t);可以將一個可變長引數變為一個list,但這個list只是一個檢視,只能讀不能新增與刪除元素。
map中的 keySet()返回的也是一個set檢視。
雜湊
對於散列表而言,有兩個引數 和 兩個方法 比軟重要。
兩個引數:
- hash值
- 雜湊因子
兩個方法:
- hashCode()
- equels()
hash演算法的好壞直接影響到了元素的分佈狀況,hash演算法不合適會使元素分佈集中。
它的原理是這樣的:
- 散列表的結構應該是這樣: ArrayList hashSet,
即第一層為陣列,然後每個陣列的元素又是一個連結串列。 - 新增第一個元素時,其hash值與陣列長度取模的值即為其存放的陣列下標。當然放於該下標下的Linkedlist的第一個元素。
- 當新增元素時,如果發生hash衝突,即陣列下標的LinkedList中已存在值,則通過equels方法決定元素是否在LinkedList的尾部新增。hashSet:相同則不新增。hashMap:相同則將原有的替換。
經過檢視HashMap程式碼,證明與猜想大致一樣。HashMap 的主要核心為 :Node<K, V>[ ] tab; 注意 tab 這是一個數組,陣列的元素為 Node<K, V>, Node 的一個節點並持有下一節點的引用(就是說它就是一個連結串列)
本以為 HashSet 也會像 HashMap 一樣來搞,檢視原始碼之後也是眼前一亮了:HashSet 的核心竟然直接就是 HashMap。它將的 map 的V 都設定成一個Object物件,然後利用map的K不能重複的特性達到set的目的,也就是說:map中的K的集合就是 set 的最終結果。
樹
- TreeSet
- Treemap
兩個都是通過紅黑樹實現的。與散表不同的是:
- 這兩個再新增時就將元素排序好了
- 沒有雜湊新增和訪問快
佇列
- ArrayDeque
- LinkedList
- ProrityQueue
1, 2 都是雙端佇列,是Deque的具體實現類,必要時可以增加長度
3 是優先順序佇列,通過堆排序來實現的。在使用時,要麼元素類實現Comparator介面,要麼通過ProrityQueue的構造方法,傳遞一個Comparator物件。
檢視
檢視是對介面的包裝,只能呼叫介面中的方法。
- 不可修改社圖
- Set Collections.unmodifiableSet(Set s); //類似還有list, map 等
- 對檢視的修改會丟擲異常
- 同步檢視
- List synchromizedList(List s);
- 多執行緒訪問list檢視時,用於修改的方法都是同步的
- 檢查檢視
- <K, V> Map checkedMap(Map<K, V> m);
- 如果新增一個錯誤的型別元素時,丟擲異常
集合取交集
Set result = new hashSet(a);
result.retainAll(b);
a, b 為Collection子類。
集合變陣列
集合的 toArray()方法返回的是Object陣列。
正確處理泛型的方法如下:
Type[] typs = 集合物件.toArray(new Type[0]);
Collections 提供了不少有用的操作集合的方法。
集合的執行緒同步正確做法
// 普通的集合類
List<Integer> list = new ArrayList();
// 同步集合檢視
List<Integer> synchronizedList = Collections.synchronizedList(list);
/*
1. 建立5個執行緒,並啟動;每個執行緒向lis中新增100個數
2. 執行緒不同步 test1 直接丟擲了 ArrayIndexOutOfBoundsException 異常
3. 執行緒同步 test2 可以列印正確結果;並且 test2 中的執行緒操作的都是 synchronizedList,但是最終其實是把資料新增到了 list 中
所以最後 list 的長度是 500.
*/
@Test
public void test1() throws InterruptedException {
for (int i = 0; i < 5; i++) {
new Thread(() -> {
for (int j = 0; j < 100; j++) {
list.add(j);
}
}).start();
}
Thread.sleep(5000);
System.out.println(list.size());
}
@Test
public void test2() throws InterruptedException {
for (int i = 0; i < 5; i++) {
new Thread(() -> {
for (int j = 0; j < 100; j++) {
synchronizedList.add(j);
}
}).start();
}
Thread.sleep(5000);
System.out.println(synchronizedList.size());
System.out.println(list.size());
}