zoukankan      html  css  js  c++  java
  • 【Java】 HashMap

    Java HashMap

    标签(空格分隔): Java source-code hash-map


    总结

    1. HashTable的基本数据结构
    2. Entry的hash与table的长度计算indexFor才能算出entry在table中的链表头节电位置
    3. HashMap中比较key是否相同的算法 e.hash == hash && ((k = e.key) == key || key.equals(k))
    4. == 与 equal
    5. hashMap的遍历方式,hashMap本身没有实现Iteratort,提供了三个set,entrySet keySet vaulues,三者都是基于HashIterator,都是获取entry进行数据访问的。

    HashMap 的数据结构模型

    Imgur

    table: Entry[],Entry的数组,table[i]里存着hash(key)对应着某个hash值的链表的头指针。
    Entry:范型数据结构,就是一个链表的节点,key,value,next(指向下一个节点),hash

    放入Put

    加入一个key-value键值对,即要加入一个entry。首先计算entry的hash,计算该hash应该存在table中的那个链表中(index(hash,table.length)) ,从头节点开始寻找

    1. 若已经有存在的 key,则替换将该entry的value更新,返回oldvalue
    2. 若为新的key,构建新的entry,加入此hash对应的链表中

    查看key的比较

    Java 示例:

    for (Entry<K,V> e = table[i]; e != null; e = e.next) {
                Object k;
                if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
                    V oldValue = e.value;
                    e.value = value;
                    e.recordAccess(this);
                    return oldValue;
                }
            }
    

    所以说Key为自定义的类时,要注意==与equals方法。

    equals 与 == 
    1.基本数据类型,也称原始数据类型。
         byte,short,char,int,long,float,double,boolean 
         他们之间的比较,应用双等号(==),比较的是他们的值。 
    2.复合数据类型(类)      
       当他们用(==)进行比较的时候,比较的是他们在内存中的存放地址,所以,除非是同一个new出来的对象,他们的比较后的结果为true,否则比较后结果为false。
       JAVA当中所有的类都是继承于Object这个基类的,在Object中的基类中定义了一个equals的方法,这个方法的初始行为是比较对象的内存地 址,但在一些类库当中这个方法被覆盖掉了,如String,Integer,Date在这些类当中equals有其自身的实现,而不再是比较类在堆内存中的存放地址了。
       对于复合数据类型之间进行equals比较,在没有覆写equals方法的情况下,他们之间的比较还是基于他们在内存中的放位置的地址值的,因为Object的equals方法也是用双等号(==)进行比较的,所以比较后的结果跟双等号(==)的结果相同。
    

    HashMap中存入的是新的值是,会

    addEntry(hash, key, value, i);
    return null;
    
    void addEntry(int hash, K key, V value, int bucketIndex) {
            if ((size >= threshold) && (null != table[bucketIndex])) {
                resize(2 * table.length);
                hash = (null != key) ? hash(key) : 0;
                bucketIndex = indexFor(hash, table.length);
            }
    
            createEntry(hash, key, value, bucketIndex);
        }
    

    resize之后,要进行旧表到新表的transfer,注意两点,

    1 旧的一条链表保存的是按照原来hash值计算出来的index(hash, table.length)一致,换成新表后,链表结构要改变,并不是单纯的把链表挪位置。想想,要是单纯的把链表在table上进行挪位子,resize也就没有意义了,resize就是为了减少hash冲突的。
    2 transfer函数可以认真看下

    transfer函数

    void transfer(Entry[] newTable, boolean rehash) {
            int newCapacity = newTable.length;
            for (Entry<K,V> e : table) {
                while(null != e) {
                    Entry<K,V> next = e.next;
                    if (rehash) {
                        e.hash = null == e.key ? 0 : hash(e.key);
                    }
                    int i = indexFor(e.hash, newCapacity);
                    e.next = newTable[i];
                    newTable[i] = e;
                    e = next;
                }
            }
        }
    

    HashSet

    HashSet实现基于HashMap,key比较出来相等时候,会进行覆盖,因而保持了Set中相同的元素只出现一次。

    private transient HashMap<E,Object> map;
    
  • 相关阅读:
    MFC深入浅出读书笔记第一部分
    给对话框添加背景
    相对路径与绝对路径之比较
    线程同步的四种方式以及串口通信
    DirectShow简单入门程序
    两个对话框之间的通信
    websphere部署war包
    Struts2的基础知识
    iBatis基础知识
    Struts1的基础知识
  • 原文地址:https://www.cnblogs.com/hundan/p/3998320.html
Copyright © 2011-2022 走看看