1. 程式人生 > 其它 >「BUAA OO」Unit1

「BUAA OO」Unit1

 

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方法
    }