文章目录

  • 算法学习10——java中的map
  • map接口
  • AbstractMap抽象Map
  • 方法
  • HashMap
  • 静态类
  • 方法


算法学习10——java中的map

map接口

  • 定义了一个用来把keys映射到maps的对象,一个map不能包含重复key,每个key最多映射一个value的值
  • 这个提供三个collection视图,允许返回keys的set,values的collection,和key-value的键值对映射。
  • 如果将可变对象作为键值需要格外注意

方法

功能

int size();

返回大小

boolean isEmpty();

是否为空

boolean containsKey(Object key);

包含键

boolean containsValue(Object value);

包含值

V get(Object key);

根据键获取值

V remove(Object key);

根据键移除元素

void putAll(Map<? extends K, ? extends V> m);

放一个map进来

void clear();

清空

Set keySet();

获取keys视图

Collection values();

获取values视图

Set<Map.Entry<K, V>> entrySet();

返回所有的键值对

AbstractMap抽象Map

**keySet:**存储键值的set

transient Set<K>        keySet;

**values:**存储取值的collection

transient Collection<V> values;

方法

方法

介绍

public int size()

返回map容器内键值的多少

public boolean isEmpty()

返回容器是否为空,调用了size()方法实现

public boolean containsValue(Object value)

容器内是否包含指定的值:1. 获取entry的迭代器 2. 如果查询的value是空值,迭代元素,用==判断 3. 如果不是空值,迭代元素,用equals判断

public boolean containsKey(Object key)

判断是否包含对应的键,同上,遍历entrySet,根据空值和非空两种不同的处理方式

public V get(Object key)

同上,遍历entrySet,每个entry元素进行判断

V put(K key, V value)

未实现该方法

public V remove(Object key)

利用迭代器遍历entryset,利用迭代器删除元素

public void putAll(Map<? extends K, ? extends V> m)

不断调用put方法,把map里的每个entry塞进来

public void clear()

清除entrySet()

public Set keySet()

返回keySet,根据entrySet临时构造

public Collection values()

同样根据entrySet临时构造

public abstract Set<Entry<K,V>> entrySet()

一个未实现的抽象方法

public boolean equals(Object o)

1. ==判断;2. 判断类型,不是一个类型直接返回false;3. 判断size,大小不一致返回false;4. equals逐个判断

public int hashCode()

所有元素的hashCode的叠加

HashMap

  • hashMap是基于hashtable的map接口实现,提供所有的可选map操作,并且允许空值和空键
  • 不保证映射顺序,并且顺序随着时间推移不恒定
  • 容量是哈希表中桶的数量
  • 负载因子是自动扩容前允许的负载程度,当存储的条目超过负载因子和容量的乘积时,容量扩展为两倍
  • 实现未同步,多个线程访问hashmap时,需要在外部同步


作用

static final int DEFAULT_INITIAL_CAPACITY = 1 << 4;

默认的初始大小

static final int MAXIMUM_CAPACITY = 1 << 30;

存储的最大容量

static final float DEFAULT_LOAD_FACTOR = 0.75f;

默认的负载因子

static final int TREEIFY_THRESHOLD = 8;

转换成为树的阈值

static final int UNTREEIFY_THRESHOLD = 6;

非树化的阈值

static final int MIN_TREEIFY_CAPACITY = 64;

树化的最小容量

transient Node<K,V>[] table;

桶的数组

transient Set<Map.Entry<K,V>> entrySet;

存放entryset

transient int size;

存放的记录的多少

transient int modCount;

如果迭代器的modCount和对象的modCount不一样,快速失败。

int threshold;

进行resize的阈值

final float loadFactor;

重载因子

静态类

Node<K,V>

域:

  • final int hash;哈希值
  • final K key;键
  • V value;值
  • Node<K,V> next;下一个node的指针

方法:

  • Node(int hash, K key, V value, Node<K,V> next),构造器,给上面说的几个域赋值
  • public final K getKey() ,get方法
  • public final V getValue() ,get方法
  • public final int hashCode(),调用的是key中的hashcode方法,然后异或,和上面的hash没啥关系
  • public final V setValue(V newValue),set一个新的value,返回旧的value
  • public final boolean equals(Object o),先==判断,然后instanceof判断,然后用equals判断key和value

方法

  • static final int hash(Object key) 如果key是空,那么返回0。就是以下这段代码,首先k==null不用说,如果是null返回0。如果不是null,那么首先调用key的hashCode,放在h中。然后右移16位,再和hash进行异或。这一套操作通俗来说就是,把key的hashcode的值,高16位和低16位进行异或。说白了就是引用高位来增加扰动,使得下标更随机更均匀。添加高位异或可以增加扰动呢?hashmap的取下标函数大多数情况都是直接取低位数值,在数据量大的时候碰撞的可能性大,因此会引入高位的数字,增加随机化。
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16)
    
//等同于以下代码
int h;
if(key == null) return 0;
h = key.hashCode();
return h ^ (h>>>16);
  • **static Class<?> comparableClassFor(Object x)**返回x是否是可比较类型,并且返回类型参数
static Class<?> comparableClassFor(Object x) {
        if (x instanceof Comparable) {
            Class<?> c; Type[] ts, as; Type t; ParameterizedType p;
            //获取对象x的类型变量,赋值给c。如果是string,直接返回
            if ((c = x.getClass()) == String.class) // bypass checks
                return c;
            //获取对象实现的接口,包含类型参数信息
            if ((ts = c.getGenericInterfaces()) != null) {
                for (int i = 0; i < ts.length; ++i) {
                    //如果是一个类型接口
                    if (((t = ts[i]) instanceof ParameterizedType) &&
                        //获取接口原始类型
                        ((p = (ParameterizedType)t).getRawType() ==
                         //原始的接口类型是不是Comparable
                         Comparable.class) &&
                        //获取类型参数
                        (as = p.getActualTypeArguments()) != null &&
                        //参数类型的数量是否是1,并且是c
                        as.length == 1 && as[0] == c) // type arg is c
                        return c;
                }
            }
        }
        return null;
    }
  • **static final int tableSizeFor(int cap)**从最高位开始全部置1
static final int tableSizeFor(int cap) {
        int n = cap - 1;
        //最高2位置1
        n |= n >>> 1;
        //最高4位置1
        n |= n >>> 2;
        //最高8位置1
        n |= n >>> 4;
        //最高16位置1
        n |= n >>> 8;
        //最高32位置1
        n |= n >>> 16;
        return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
    }
  • public HashMap(int initialCapacity, float loadFactor) 构造器
public HashMap(int initialCapacity, float loadFactor) {
        if (initialCapacity < 0)
            throw new IllegalArgumentException("Illegal initial capacity: " +
                                               initialCapacity);
        if (initialCapacity > MAXIMUM_CAPACITY)
            initialCapacity = MAXIMUM_CAPACITY;
        if (loadFactor <= 0 || Float.isNaN(loadFactor))
            throw new IllegalArgumentException("Illegal load factor: " +
                                               loadFactor);
        this.loadFactor = loadFactor;
        //获取阈值
        this.threshold = tableSizeFor(initialCapacity);
    }
  • **public HashMap(int initialCapacity)**构造器
public HashMap(int initialCapacity) {
        this(initialCapacity, DEFAULT_LOAD_FACTOR);
    }
  • public HashMap(Map<? extends K, ? extends V> m)
public HashMap(Map<? extends K, ? extends V> m) {
        this.loadFactor = DEFAULT_LOAD_FACTOR;
        putMapEntries(m, false);
    }
  • **final void putMapEntries(Map<? extends K, ? extends V> m, boolean evict)**放入一个map,如果是初始化就是false,否则是true。
final void putMapEntries(Map<? extends K, ? extends V> m, boolean evict) {
        //s是输入的map的大小
        int s = m.size();
        if (s > 0) {
            //如果table还未初始化
            if (table == null) { // pre-size
                //计算除以负载因子后的容量大小
                float ft = ((float)s / loadFactor) + 1.0F;
                int t = ((ft < (float)MAXIMUM_CAPACITY) ?
                         (int)ft : MAXIMUM_CAPACITY);
                //如果t大于阈值(初始化的时候是会大于阈值的),把t从高位开始全部置1
                if (t > threshold)
                    threshold = tableSizeFor(t);
            }
            //如果table已经初始化,并且大于阈值
            else if (s > threshold)
                //扩容
                resize();
            //插入
            for (Map.Entry<? extends K, ? extends V> e : m.entrySet()) {
                K key = e.getKey();
                V value = e.getValue();
                putVal(hash(key), key, value, false, evict);
            }
        }
    }
  • **public int size()**返回数组的大小
  • **public boolean isEmpty()**是否为空
  • public V get(Object key)
public V get(Object key) {
        Node<K,V> e;
        //调用getNode方法获取value
        return (e = getNode(hash(key), key)) == null ? null : e.value;
    }
  • **final Node<K,V> getNode(int hash, Object key)**根据hash和key获取node
final Node<K,V> getNode(int hash, Object key) {
        Node<K,V>[] tab; Node<K,V> first, e; int n; K k;
        //如果table非空,并且table的长度大于0
        if ((tab = table) != null && (n = tab.length) > 0 &&
            //并且对应的桶的不为空
            (first = tab[(n - 1) & hash]) != null) {
            //如果第一个hash就相等
            if (first.hash == hash && // always check first node
                //并且key相等或者equals判断相等,那么返回first
                ((k = first.key) == key || (key != null && key.equals(k))))
                return first;
            //如果first的下一个节点非空,并且是树节点,那么递归地返回treenode
            if ((e = first.next) != null) {
                if (first instanceof TreeNode)
                    return ((TreeNode<K,V>)first).getTreeNode(hash, key);
                //利用链表结构返回node
                do {
                    if (e.hash == hash &&
                        ((k = e.key) == key || (key != null && key.equals(k))))
                        return e;
                } while ((e = e.next) != null);
            }
        }
        return null;
    }
  • **public boolean containsKey(Object key)**利用getNode判断是否包含key
  • **public V put(K key, V value)**利用putVal方法放置key,value键值对
  • **final V putVal(int hash, K key, V value, boolean onlyIfAbsent, boolean evict)**放置键值对
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
                   boolean evict) {
        Node<K,V>[] tab; Node<K,V> p; int n, i;
        //如果table为空,或者长度为0
        if ((tab = table) == null || (n = tab.length) == 0)
            //进行resize
            n = (tab = resize()).length;
        //i为hash后的坐标,首先看看这个是不是已经为空了,如果为空就直接往里塞
        if ((p = tab[i = (n - 1) & hash]) == null)
            tab[i] = newNode(hash, key, value, null);
        else {
            Node<K,V> e; K k;
            //否则判断当前节点的hash值是否相等
            if (p.hash == hash &&
                //key是否相等或者是equals
                ((k = p.key) == key || (key != null && key.equals(k))))
                e = p;
            //如果已经是树了
            else if (p instanceof TreeNode)
                //调用树的方法来put
                e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
            else {
                //通过链表结构,循环地遍历,如果遇上已经有key,那就返回节点node,直到遍历完,
                //如果发现这个链表已经到达树化的阈值,就进行树化
                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;
                }
            }
            //如果e非空,更新值
            if (e != null) { // existing mapping for key
                V oldValue = e.value;
                if (!onlyIfAbsent || oldValue == null)
                    e.value = value;
                afterNodeAccess(e);
                return oldValue;
            }
        }
        ++modCount;
        //如果值太大了,进行resize
        if (++size > threshold)
            resize();
        afterNodeInsertion(evict);
        return null;
    }
  • final Node<K,V>[] resize() 扩容函数
final Node<K,V>[] resize() {
        Node<K,V>[] oldTab = table;
    	//旧数组大小
        int oldCap = (oldTab == null) ? 0 : oldTab.length;
    	//旧的阈值
        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;
            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];
        table = newTab;
        if (oldTab != null) {
            for (int j = 0; j < oldCap; ++j) {
                Node<K,V> e;
                if ((e = oldTab[j]) != null) {
                    //先把原位置置空
                    oldTab[j] = null;
                    //如果原元素的下一位是空的,直接重新hash就好了
                    if (e.next == null)
                        newTab[e.hash & (newCap - 1)] = e;
                    //如果是TreeNode
                    else if (e instanceof TreeNode)
                        //利用树重hash
                        ((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;
                        //扩容是扩容一倍嘛,多了一位bit,扩容思路是按照这个bit位,分别分散到两个位置里去
                        do {
                            next = e.next;
                            //如果e的hash与旧的大小为0
                            if ((e.hash & oldCap) == 0) {
                                //尾节点为空
                                if (loTail == null)
                                    //头节点就是e
                                    loHead = e;
                                else
                                    //尾节点的下一个节点就是e
                                    loTail.next = e;
                                //尾节点是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;
    }