EighteenDay-java Map及其實現類HashMap、Hashtable
1、Map<k,v>介面的常用方法
V put(K key, V value) 新增鍵值對
void clear() 清除所有鍵值對
boolean containsKey(Object key) 是否包含指定鍵 是就返回true
boolean containsValue(Object value) 是否包含指定值 是就返回true
V get(Object key) 以鍵找值 返回值
boolean isEmpty() 此地圖是否包含鍵值對 不包含返回true
Set<K> keySet() 獲取所有的鍵 以set集合形式返回
V remove(Object key) 以鍵刪除鍵值對
int size() 此地圖中含有多少個鍵值對
Set<Map.Entry<K,V>> entrySet() 把Map轉換成Set集合
Map: 1:zhangsan 2:lisi 3:wangwu
Set: 1=zhangsan 2=lisi 3=wangwu [它們的型別是Map.Entry<k,v>]
public static void main(String[] args) {
//建立Map集合物件
Map<Integer,String> map=new HashMap<>();
//新增鍵值對
map.put(1,"zhangsan");
map.put(2,"wangwu");
map.put(3,"lisi");
map.put(4,"liuliu");
//Map中鍵值對的個數
System.out.println(map.size());//4
//以鍵找值
System.out.println(map.get(1));//zhangsan
//包含鍵
System.out.println(map.containsKey(3));//true
//包含值
System.out.println(map.containsValue("zhangsan"));//true
//刪除鍵值對
String v=map.remove(2);
System.out.println(v);//wangwu
//是否為空
System.out.println(map.isEmpty());//false
//返回所有鍵
Set<Integer> ints = map.keySet();
for (Integer anInt : ints) {
System.out.print(anInt+" ");//1 3 4
}
2、有關Map取鍵值對
鍵值對的第一種獲取方式:先獲取所有的鍵,在獲取值
第二種方式 把鍵值對一起取出來(效率較高)
public static void main(String[] args) {
Map<Integer,String> map=new HashMap<>();
map.put(1,"wo");
map.put(2,"hao");
map.put(3,"ni");
//鍵值對的第一種獲取方式:先獲取所有的鍵,在獲取值
//獲取所有鍵
Set<Integer> keys = map.keySet();
/* //迭代器
Iterator<Integer> k= keys.iterator();
//迭代
while(k.hasNext()){
Integer k1 = k.next();
System.out.println(k1+":"+map.get(k1));
}
*/
//foreach
for (Integer key : keys) {
System.out.print(key+":"+map.get(key)+" ");//1:wo 2:hao 3:ni
}
System.out.println();
//第二種方式 把鍵值對一起取出來
Set<Map.Entry<Integer, String>> set = map.entrySet();
for (Map.Entry<Integer, String> node: set) {
System.out.print(node.getKey()+":"+node.getValue()+" ");//1:wo 2:hao 3:ni
}
}
3、HashMap
1>、HsahMap集合底層是雜湊表/散列表的資料結構
2>、雜湊表是一個數組和單向列表的結合體
陣列在查詢方面效率很高,連結串列在增刪方面效率很高
3>、HsahMap底層原始碼
//HashMap底層實際上是一個一維陣列
Node<k,v>[] table;
//靜態內部類HashMap.Node
static class Node<K,V> implements Map.Entry<K,V> {
final int hash;//雜湊值,是key的hashCode()方法執行結果。hash值通過演算法函式,可以轉換成地址
final K key;//儲存到Map集合中的key
V value;//儲存到Map集合中的Value
Node<K,V> next;//下一個節點的記憶體地址
}
雜湊表/散列表:一維陣列,這個陣列中的每一個元素都是一個單向連結串列
4、HashMap集合的存取原理
Map中的put(k,v)方法原理:
接收k,v後,封裝到Node<k,v>物件中,呼叫k的hashCode()方法,得到K的雜湊值,通過雜湊函式與演算法生成陣列下標值,找到對應的陣列下標位置,若為空,則直接把鍵值對放進去,若不為空,則一一比較在此陣列下標 位置上的連結串列的k值,使用equals,返回true則把v值用新的覆蓋,返回false,則把此鍵值對放在連結串列的末尾
Map中的get(k)方法原理:
接收到k值後 底層呼叫hashCode方法,得到k的雜湊值後通過雜湊函式和演算法生成陣列下標,通過陣列下標快速定位到連結串列在陣列中的位置,若此位置為null則返回null,若不為空,拿k與單鏈表一一比較通過equals ,返回true則拿出k對應的v值,返回false 則返回null。
綜上知,若要自定義引用型別,使用集合,一定要重寫equals和hashCode方法。
如果hashCode返回的值只有一個,則此雜湊表為純連結串列,屬於不均勻散列表
分佈均勻散列表:有100個元素,陣列空間為10,則每個空間有10個;
如果hashCode返回值每次都返回不一樣的值,則雜湊表為純陣列,屬於不均勻散列表
重點:HashMap集合的預設初始化容量是16,預設因子是0.75 預設因子:當HashMap底層陣列的容量達到75%的時候,陣列開始擴 容,HashMap 集合的初始化容量必須是2的倍數,因為為了達到雜湊 均勻,調高HashMap集合的存取效率
如圖所示
(圖為B站老師所畫)
HashMap集合的key特點
無序不可重複:
無序:因為不一定掛到那個連結串列上
不可重複:equals保證了key不會重複
*JDK8之後在雜湊表中當單向連結串列超過8個,則資料結構會變為紅黑樹結構,當紅黑樹節點小於6時又變回連結串列
5、Hashtable
Hashtable可以為空嗎?
Hashtable的鍵與值都不可以為null
HashMap的鍵與值可以為null
Hashtable方法都帶有synchronized:執行緒安全的。執行緒安全有其他方案,這個Hashtable對執行緒的處理效率較低,運用較少了。
底層都是雜湊表,初始化容量是11,預設載入容量0.75f,擴容是原容量乘以2加1.
public static void main(String[] args) {
Map map=new Hashtable();
map.put(null,100);//NullPointerException異常
System.out.println(map.size());
}
6、Properties
Properties屬性類物件的相關方法:
Properties是Map的一個集合,繼承Hashtable
Properties的key與value都是String型別的
Properties被稱為屬性類物件
//存
properties.setProperty("url","jdbc:mysql://localhost:3306/product");
//取
String url = properties.getProperty("url");
System.out.println(url);//jdbc:mysql://localhost:3306/product
7、TreeSet
1>、TreeSet集合無序不重複,但可以按大小排序,底層是TreeMap
2>、TreeMap的底層是二叉樹
3>、TreeSet儲存資料相當於在TreeMap的key裡儲存
4>對於自定義型別來說,可以排序嗎?
無法排序,因為沒有指定排序規則 會出現ClassCastException異常:class EighteenDay.student cannot be cast to class java.lang.Comparable
在底層程式碼中,HashMap的put方法會進行鍵的比較(compareTo()方法),鍵值在比較前會強制轉換成Comparable比較器型別,而student為自定義型別 沒有實現比較器介面,故無 法轉型,報錯。
總結:在使用TreeSet/TreeMap集合儲存自定義型別資料時,自定義類一定要繼承Java.lang.Comparable,重寫compareTo方法進行排序規則的制定
public static void main(String[] args) {
student st=new student(13);
student st2=new student(12);
TreeSet<student> ts=new TreeSet<>();
ts.add(st);
ts.add(st2);
System.out.println(ts.size());
for (student t : ts) {
System.out.println(t);
}
}
class student implements Comparable<student>{
int age;
public student(int age) {
this.age = age;
}
//重寫compareTo 制定排序規則
@Override
public int compareTo(student o) {
return this.age-o.age;//這個是降序 o.age-this.age 就順序相反了(升序)
/*底層程式碼以二叉樹的形式排序,我們需要在這裡重寫compareTo返回三種情況的某一種,負數,0,正數
cmp = k.compareTo(t.key);
if (cmp < 0)
t = t.left;
else if (cmp > 0)
t = t.right;
else
return t.setValue(value);
*/
}
//重寫toString方法
@Override
public String toString() {
return "age=" + age;
}
}