「BUAA OO」Unit1
阿新 • • 發佈:2022-03-26
Map父介面:
特點:儲存一對資料(key-value),無序,無下標,鍵不可重複,值可重複。
方法:V put(K key,V value) //將物件存入到集合中,關聯鍵值。key重複則覆蓋原值。
Object get(Object key) //根據鍵獲取對應的值
Set<K> keySet() //返回所有key。
Collection<V> values() //返回包含所有值的Colletion集合
Set<Map.Entry<K,V>> entrySet() //鍵值匹配的Set集合
package com.java.leetcode.collection; import java.util.HashMap; import java.util.Map; import java.util.Set; /* Map介面的使用 特點;儲存鍵值對;鍵不能重複,值可以重複;無序 */ public class HMap01 { public static void main(String[] args) { Map map = new HashMap(); map.put("apple","蘋果"); map.put("peach","桃"); map.put("pear","梨"); map.put("plum","李子"); map.put("pear","李子");//重複的key。後面的value會取代之前的value System.out.println(map.toString());//{plum=李子, apple=蘋果, pear=李子, peach=桃} 無序的 //刪除 map.remove("pear"); //刪除鍵為pear的資料 System.out.println("刪除之後:"+map); /* 遍歷;1.使用keySet()方法 Set<K> keySet() //返回所有key。*/ System.out.println("*****keySet()遍歷Map*****"); Set<String> keySet = map.keySet(); //對set集合可用增強for或者迭代器遍歷 for(String s : keySet){ System.out.println(s +"---"+map.get(s));// Object get(Object key) //根據鍵獲取對應的值 } /* 使用entrySet方法 Set<Map.Entry<K,V>> entrySet() //鍵值匹配的Set集合 */ System.out.println("*****entrySet()遍歷Map*****"); Set<Map.Entry<String,String>> entries = map.entrySet(); //可用增強for,迭代器進行遍歷 for(Map.Entry<String,String> entry : entries){ System.out.println(entry.getKey()+"---"+entry.getValue()); } //entrySet()方法效率高於keySet() //判斷 System.out.println("是否存在鍵為pear?"+map.containsKey("pear")); System.out.println("是否存在值為桃?"+map.containsValue("桃")); } }
執行結果:
HashMap:
基於雜湊表的Map介面的實現。此實現提供所有可選的對映操作,並允許使用null鍵和null值。
構造方法:
HashMap() 構造一個具有預設初始容量(16)和預設載入因子(0.75)的空HahMap //載入因子0.75:假如現在容量為100,當資料元素超過75的時候,就開始擴容。
.....
package com.java.leetcode.collection; import java.util.HashMap; import java.util.Map; import java.util.Set; /* HashMap的使用 儲存結構:雜湊表(陣列+連結串列) 在JDK1.8之後為陣列+連結串列+紅黑樹 */ public class HMap02 { public static void main(String[] args) { HashMap<Student,String> hashMap = new HashMap<Student,String>(); Student s1 = new Student("張三","男"); Student s2 = new Student("李四","男"); Student s3 = new Student("小芳","女"); hashMap.put(s1,"北京");//將Student作為key;地址作為value hashMap.put(s2,"上海"); hashMap.put(s3,"深圳"); hashMap.put(s3,"南京"); //重複的key。後面的value會覆蓋前面的value System.out.println(hashMap); hashMap.put(new Student("小芳","女"),"杭州"); //??通過new Student的方式。將key值一樣的資料成功新增。 System.out.println(hashMap);//通過new Student的方式,要想key已經存在的不能新增只能覆蓋。需要重寫equals和hashcode.直接快捷鍵 //刪除 hashMap.remove(s1); System.out.println("刪除之後:"+hashMap); //遍歷 System.out.println("*****keySet()遍歷Map*****"); Set<Student> set = hashMap.keySet(); for(Student s : set){ System.out.println(s+"---"+hashMap.get(s)); } System.out.println("*****entrySet()遍歷Map*****"); Set<Map.Entry<Student,String>> entries = hashMap.entrySet(); for(Map.Entry<Student,String> entry : entries){ System.out.println(entry.getKey()+"---"+entry.getValue()); } //判斷...啥的不寫了 } }
執行結果:
原始碼分析:
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16 預設初始容量大小:1左移四位
static final int MAXIMUM_CAPACITY = 1 << 30; // 陣列最大容量大小:1^30
static final float DEFAULT_LOAD_FACTOR = 0.75f; //預設載入因子:0.75
static final int TREEIFY_THRESHOLD = 8; //JDK1.8之後加入。是從連結串列轉成樹的閾值。調整成紅黑樹
static final int UNTREEIFY_THRESHOLD = 6; //當長度降到6時,就轉回連結串列
static final int MIN_TREEIFY_CAPACITY = 64; //最小的陣列容量大小。當連結串列長度大於8,並且集合元素個數大於等於64時,調整為紅黑樹
transient Node<K,V>[] table; //雜湊表中的陣列
transient int size; //元素個數
HashMap<Student,String> hashMap = new HashMap<Student,String>(); //剛建立時,還沒有新增元素。table=null size = 0;
hashMap.put(s1,"北京"); //使用put方法新增第一個元素時。
public V put(K key, V value) { return putVal(hash(key), key, value, false, true); }
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
//雜湊值
final V putVal(int hash, K key, V value, boolean onlyIfAbsent, boolean evict) { Node<K,V>[] tab; Node<K,V> p; int n, i; if ((tab = table) == null || (n = tab.length) == 0) //此時table為null, n = (tab = resize()).length; //n=16,此時一個元素都還沒有 if ((p = tab[i = (n - 1) & hash]) == null) tab[i] = newNode(hash, key, value, null);//用newNode方法將元素加進陣列,根據hash判斷位置 else { Node<K,V> e; K k; if (p.hash == hash && ((k = p.key) == key || (key != null && key.equals(k)))) e = p; else if (p instanceof TreeNode) e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value); else { for (int binCount = 0; ; ++binCount) { if ((e = p.next) == null) { p.next = newNode(hash, key, value, null); if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st treeifyBin(tab, hash); break; } if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) break; p = e; } } if (e != null) { // existing mapping for key V oldValue = e.value; if (!onlyIfAbsent || oldValue == null) e.value = value; afterNodeAccess(e); return oldValue; } } ++modCount; if (++size > threshold) //當元素個數大於16*0.75=12時,就開始調整陣列大小。每次調整都是原來的2倍。 resize(); afterNodeInsertion(evict); return null; }
final Node<K,V>[] resize() { Node<K,V>[] oldTab = table; //null int oldCap = (oldTab == null) ? 0 : oldTab.length; //oldCap =null int oldThr = threshold; int newCap, newThr = 0; if (oldCap > 0) { if (oldCap >= MAXIMUM_CAPACITY) { threshold = Integer.MAX_VALUE; return oldTab; } else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY && //新的大小 = 舊的大小左移一位;即舊大小的兩倍 oldCap >= DEFAULT_INITIAL_CAPACITY) newThr = oldThr << 1; // double threshold } else if (oldThr > 0) // initial capacity was placed in threshold newCap = oldThr; else { //進入這裡 // zero initial threshold signifies using defaults newCap = DEFAULT_INITIAL_CAPACITY; //newCap =16 newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY); } if (newThr == 0) { float ft = (float)newCap * loadFactor; newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ? (int)ft : Integer.MAX_VALUE); } threshold = newThr; @SuppressWarnings({"rawtypes","unchecked"}) Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap]; //建立了一個新的陣列,大小為16 table = newTab; //大小16 if (oldTab != null) { for (int j = 0; j < oldCap; ++j) { Node<K,V> e; if ((e = oldTab[j]) != null) { oldTab[j] = null; if (e.next == null) newTab[e.hash & (newCap - 1)] = e; else if (e instanceof TreeNode) ((TreeNode<K,V>)e).split(this, newTab, j, oldCap); else { // preserve order Node<K,V> loHead = null, loTail = null; Node<K,V> hiHead = null, hiTail = null; Node<K,V> next; do { next = e.next; if ((e.hash & oldCap) == 0) { if (loTail == null) loHead = e; else loTail.next = e; loTail = e; } else { if (hiTail == null) hiHead = e; else hiTail.next = e; hiTail = e; } } while ((e = next) != null); if (loTail != null) { loTail.next = null; newTab[j] = loHead; } if (hiTail != null) { hiTail.next = null; newTab[j + oldCap] = hiHead; } } } } } return newTab; }
總結;
- 當HashMap剛建立時,table是null,為了節省空間,當新增第一個元素時,table容量調整為16
- 當元素個數大於閾值(16*0.75 = 12)時,會進行擴容,擴容後大小為原來的2倍,目的是減少調整元素的個數
- jdk1.8 當每個連結串列長度大於8,並且陣列元素個數大於等於64時,會調整為紅黑樹,目的是提高執行效率
- jdk1.8 當連結串列長度小於6時,調整成連結串列
- jdk1.8以前,連結串列是頭插入,jdk1.8以後是尾插入
HashSet與HashMap
public HashSet() { map = new HashMap<>(); }
public boolean add(E e) { return map.put(e, PRESENT)==null; //實質是呼叫map中的put方法 }
HashMap:
- JDK1.2版本,執行緒不安全,執行效率快;允許用null作為key或是value
Hashtable:
- JDK1.0版本,執行緒安全,執行效率慢;不允許null作為key或是value
- 無參構造:用預設初始容量:11和載入因子:0.75構造一個新的空雜湊表。
Properties:
- Hashtable的子類,要求key和value都是String.通常用於配製檔案的讀取
- 表示了一個持久的屬性集。可以儲存在流中或從流中載入。
TreeMap:
- 實現了SortedMap介面(是Map的子介面),可以對key自動排序
TreeMap:
package com.java.leetcode.collection; import java.util.TreeMap; /* TreeMap的使用 儲存結構:紅黑樹 */ public class TMap { public static void main(String[] args) { TreeMap<Student,String> treeMap = new TreeMap<Student,String>(); Student s1 = new Student("張三","男"); Student s2 = new Student("李四","男"); Student s3 = new Student("小芳","女"); treeMap.put(s1,"北京"); treeMap.put(s2,"上海"); treeMap.put(s3,"杭州"); System.out.println(treeMap); //會直接掛掉。報型別轉換異常。因為treeMap儲存結構是紅黑樹,需要指定比較方式,實現Comparable介面,或者Comparator treeMap.put(new Student("李四","男"),"北京"); //此時不需要重寫equals和hashcode方法也會新增失敗。因為這裡是用compare比較的,此處key相同,後面的value會覆蓋前面的value System.out.println(treeMap); //遍歷啥的不寫了....用keySet()或者entrySet() } }
實現Comparable介面;
package com.java.leetcode.collection; public class Student implements Comparable<Student>{ private String name; private String sex; @Override public String toString() { return "Student{" + "name='" + name + '\'' + ", sex='" + sex + '\'' + '}'; } public Student(String name, String sex) { this.name = name; this.sex = sex; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getSex() { return sex; } public void setSex(String sex) { this.sex = sex; } @Override public int compareTo(Student o) { int nn = this.name.compareTo(o.name); int cn = this.sex.compareTo(o.sex); return nn == 0 ?cn:nn; } }
//還可以通過實現Comparator:實現定製比較(比較器)
TreeMap<Student,String> treeMap = new TreeMap<Student,String>(new Comparator<Student>() { @Override public int compare(Student o1, Student o2) { int nn = o1.getName().compareTo(o2.getName()); int cn = o1.getSex().compareTo(o2.getSex()); return nn == 0 ?cn:nn; } });
TreeSet和TreeMap
public TreeSet() { this(new TreeMap<E,Object>()); //實質上是用了TreeMap }
public boolean add(E e) { return m.put(e, PRESENT)==null; //實際是用了Map裡是put方法 }