Map集合

 

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

 

posted @ 2022-03-29 16:34  虞美人体重90  阅读(23)  评论(0编辑  收藏  举报