摘要:繼承的類,泛型為時,注意和其他的類型不同。因為是線程安全簡單來說,是個一維數組。同樣,指定和,如果中間發生變化則會拋出異常。最后,可以,然后,使用基類可以實現和的快速賦值。線程安全也是線程安全的,和一樣,連函數都喪心病狂地同步了。
這么幾個比較常用的但是比較容易混淆的概念同出于 java.util 包。本文僅作幾個類的淺度解析。
(本文基于JDK1.7,源碼來自openjdk1.7。)
├── Collection │?? ├── List │ │?? ├── ArrayList │?? │ ├── Vector │?? │ └── LinkedList and so on; │ Set │?? ├── HashSet │?? └── LinkedHashSet and so on; └── Map ?? ├── Hashtable ?? ├── HashMap ?? └── WeakHashMap and so on;Collection和Map
按理來說和這兩者沒有什么特別關系。然而本文中仍然將這兩個混在一起:
第一,HashSet,HashMap,Hashtable長得太像了,沒有理由不寫再一起。
第二,HashSet是個Set,其實骨子里是個Map。
List繼承List的類,泛型為Integer時,注意和其他的類型不同。(因為Index是Integer)
線程安全簡單來說,List是個一維數組。Vector和ArrayList區別在于:
Vector是個線程安全的,而ArrayList不是。這點看源碼可以看出來,Vector中各種synchronized,甚至連size屬性都喪心病狂地synchronized了。
同樣,Iterator指定Vector和ArrayList,如果中間發生變化則會拋出ConcurrentModificationException異常。
不僅僅是List,很多地方的Iterator都有這個異常,
ArrayList沒辦法設置自增大小,但是仍然可以自增。
Vector可以設置自增大小。
ArrayList的自增函數:
Vector的自增函數:
private void grow(int minCapacity) { // overflow-conscious code int oldCapacity = elementData.length; int newCapacity = oldCapacity + ((capacityIncrement > 0) ? capacityIncrement : oldCapacity); //在這里,如果沒有設置自增大小的話,那么默認自增大小則是原來的Vector大小。 if (newCapacity - minCapacity < 0) newCapacity = minCapacity; if (newCapacity - MAX_ARRAY_SIZE > 0) newCapacity = hugeCapacity(minCapacity); elementData = Arrays.copyOf(elementData, newCapacity); }
ArrayList自增函數:
private void grow(int minCapacity) { // overflow-conscious code int oldCapacity = elementData.length; int newCapacity = oldCapacity + (oldCapacity >> 1); //新長度則是舊長度的大小加上一個移位運算(一半的大小)。 if (newCapacity - minCapacity < 0) newCapacity = minCapacity; if (newCapacity - MAX_ARRAY_SIZE > 0) newCapacity = hugeCapacity(minCapacity); // minCapacity is usually close to size, so this is a win: elementData = Arrays.copyOf(elementData, newCapacity); }
所以可以簡單的認為Vector默認自增一倍的長度,ArrayList則是默認自增一半的長度。
同樣,對他們的源碼分析:
Vector可以設置自增大小,而ArrayList則無法設置(沒有這個構造方法)。
public Vector(int initialCapacity, int capacityIncrement)
最后,可以 List list=new ArrayList<>();,然后list=new Vector<>();,使用基類List可以實現ArrayList和Vector的快速賦值。
MapMap可以當成是簡單的“Key-Value”數據庫,例如memcached和redis。
線程安全Hashtable基于Directory,有種說法是Directory已經過時,所以更推薦使用Map。
Hashtable也是線程安全的,和Vector一樣,連size函數都喪心病狂地同步了。
public synchronized int size() { return count; }
HashMap不是線程安全的,當然也可以用其他的黑科技實現同步,例如SynchronizedMap和ConcurrentHashMap。
那個以后再說……
HashMap允許在Key和Value中都出現null值。例如:
v.toString() {null=null, 1=66, 2=null, 3=4d, 4=0f, 5=null}
而Hashtable則不允許K/V的任何一個出現null值,但是允許空字符串。
Hashtablev=new Hashtable (); v.put("3s",null);
直接報錯:java.lang.NullPointerException。
這個和內部實現有關,Hashtable需要用到hashCode,所以必須要確保K/V不為null。
相關代碼寫死在源碼里:
public synchronized V put(K key, V value) { // Make sure the value is not null if (value == null) { throw new NullPointerException(); } //下面對hashCode做點事情。Set
HashSet本質上是一個Collection,類似于List,是列表/集合,不是K-V的Map,但是它骨子里是一個HashMap……
這么說可能會更易于理解:
HashSet對外是“類”的集合,實際上是內部維護了一個HashMap進行實現。
實際上存儲的是兩個:hashCode和類本身(字符串/自定義類等)。
HashSet進行add的時候,會先進行驗證hashCode:
(HashSet進行add操作實際上是對Map的put操作)
public V put(K key, V value) { if (table == EMPTY_TABLE) { inflateTable(threshold); } if (key == null) return putForNullKey(value); int hash = hash(key);//本函數里有hashCode int i = indexFor(hash, table.length); for (Entry一個對HashSet的自定義類的操作:e = table[i]; e != null; e = e.next) { Object k; if (e.hash == hash && ((k = e.key) == key || key.equals(k))) { V oldValue = e.value; e.value = value; e.recordAccess(this); return oldValue; } } modCount++; addEntry(hash, key, value, i); return null; }
class Name { private String first; private String last; public Name(String first, String last) { this.first = first; this.last = last; } public boolean equals(Object o) { if (this == o) { return true; } if (o.getClass() == Name.class) { Name n = (Name)o; return n.first.equals(first) && n.last.equals(last); } return false; } } public class HashSetTest { public static void main(String[] args) { Sets = new HashSet (); s.add(new Name("abc", "123")); System.out.println( s.contains(new Name("abc", "123"))); } }
代碼來自java中HashSet詳解
粗看上去是會返回true的,實際返回false。因為 HashSet 判斷兩個對象相等的標準除了要求通過 equals() 方法比較返回 true 之外,還要求兩個對象的 hashCode() 返回值相等。而上面程序沒有重寫 Name 類的 hashCode() 方法,兩個 Name 對象的 hashCode() 返回值并不相同,因此 HashSet 會把它們當成 2 個對象處理,因此程序返回 false。如果想返回true,需要重寫equals方法和hashCode方法。
集合操作至于hashCode,就是另一篇文章才解釋得清的了。
Set其實做集合操作更加簡單。例如:
import java.util.*; public class SetOperation { public static void main(String[] args) { Setresult = new HashSet (); Set set1 = new HashSet (){{ add(1); add(3); add(5); }}; Set set2 = new HashSet (){{ add(1); add(2); add(3); }}; result.clear(); result.addAll(set1); result.retainAll(set2); System.out.println("交集:"+result); result.clear(); result.addAll(set1); result.removeAll(set2); System.out.println("差集:"+result); result.clear(); result.addAll(set1); result.addAll(set2); System.out.println("并集:"+result); } }
輸出:
交集:[1, 3] 差集:[5] 并集:[1, 2, 3, 5]參考文檔
hashSet中的equals方法和hashCode方法
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/64382.html
摘要:若遇到哈希沖突,則將沖突的值加到鏈表中即可。之后相比于之前的版本,之后在解決哈希沖突時有了較大的變化,當鏈表長度大于閾值默認為時,將鏈表轉化為紅黑樹,以減少搜索時間。有序,唯一紅黑樹自平衡的排序二叉樹。 本文是最最最常見Java面試題總結系列第三周的文章。主要內容: Arraylist 與 LinkedList 異同 ArrayList 與 Vector 區別 HashMap的底層...
摘要:是哈希表實現的,中的數據是無序的,可以放入,但只能放入一個,兩者中的值都不能重復,就如數據庫中唯一約束。四和的相同點和區別相同兩者都是基于哈希表實現的內部也都是通過單鏈表解決沖突問題同樣實現了序列化和克隆接口區別繼承的父類不同。 一.Arraylist與LinkedList有什么區別? 1、ArrayList是實現了基于動態數組的數據結構,因為地址連續,一旦數據存儲好了,查詢操作效率...
閱讀 2341·2021-11-22 14:56
閱讀 1482·2021-09-24 09:47
閱讀 915·2019-08-26 18:37
閱讀 2832·2019-08-26 12:10
閱讀 1531·2019-08-26 11:55
閱讀 3151·2019-08-23 18:07
閱讀 2308·2019-08-23 14:08
閱讀 614·2019-08-23 12:12