精华内容
下载资源
问答
  • javaHashMap

    2011-11-29 18:35:11
    java.util.HashMap类提供了静态的hash方法和indexFor方法: /*** Applies a supplemental hash function to a given hashCode, which* defends against poor quality hash functions. This is critical* because ...

    java.util.HashMap类提供了静态的hash方法和indexFor方法:

    /**
    * Applies a supplemental hash function to a given hashCode, which
    * defends against poor quality hash functions. This is critical
    * because HashMap uses power-of-two length hash tables, that
    * otherwise encounter collisions for hashCodes that do not differ
    * in lower bits. Note: Null keys always map to hash 0, thus index 0.
    */
    static int hash(int h) {
    // This function ensures that hashCodes that differ only by
    // constant multiples at each bit position have a bounded
    // number of collisions (approximately 8 at default load factor).
    h ^= (h >>> 20) ^ (h >>> 12);
    return h ^ (h >>> 7) ^ (h >>> 4);
    }

    /**
    * Returns index for hash code h.
    */
    static int indexFor(int h, int length) {
    return h & (length-1);
    }

    看hash方法的javadoc可以了解其目的:为了防止低质量的hash方法带来的过多冲突而增加的这个静态的hash方法。核心代码:

    h ^= (h >>> 20) ^ (h >>> 12);
    return h ^ (h >>> 7) ^ (h >>> 4);

    非常的晕,不是吗?还好总有一些牛人走在我们前面进行了分析:

    http://nbqwcnm.iteye.com/blog/1126938

    (我的理解)简单点说,就是通过上面代码把原本由低质量的hash方法产生的h进行再次hash而得到一个新的整数,这个整数的产生依赖于对原h的每个字节(共8个)按照一定算法进行^处理,从而保证冲突的尽量避免。

    每个字节的处理过程如下:

    8=8 
    7=7^8 
    6=6^7^8 
    5=5^8^7^6 
    4=4^7^6^5^8 
    3=3^8^6^5^8^4^7 
    2=2^7^5^4^7^3^8^6 
    1=1^6^4^3^8^6^2^7^5 
    结果中的1、2、3三位出现重复位^运算 
    3=3^8^6^5^8^4^7     ->   3^6^5^4^7 
    2=2^7^5^4^7^3^8^6   ->   2^5^4^3^8^6 
    1=1^6^4^3^8^6^2^7^5 ->   1^4^3^8^2^7^5 

     

    再来看看indexFor方法

    static int indexFor(int h, int length) {
    return h & (length-1);
    }

    通过hash值h(由HashMap.hash方法确定的)和hash表长度length来确定当前hash值在hash表中的位置。

    照理说我们最常见的做法应该是h%length就可以了,而这里为什么要h&(length-1)呢?其实还是从性能的角度考虑的。我们知道如果Hash表长度是2的平方的话h&(length-1)与h%length是等价的,而前者的性能要高很多。

    http://nbqwcnm.iteye.com/blog/1126988

     

    展开全文
  • javahashmap添加时遇到的问题 引入:为什么hashmap中添加时用了hashcode()还要用equals()去判断桶内部是否键对象相等,相等时替换旧值,而不相等时用头插法在链表头部插入新对象。 哈希码由于只有2 ^ 32个不同的整数...

    javahashmap添加时遇到的问题

    引入:为什么hashmap中添加时用了hashcode()还要用equals()去判断桶内部是否键对象相等,相等时替换旧值,而不相等时用头插法在链表头部插入新对象。

    哈希码由于只有2 ^ 32个不同的整数,并且在任何VM实例中都可能有2 ^ 32个以上的活动对象,因此从技术上讲,不可能为每个对象保证唯一的哈希码。

    即默认哈希码可能基于对象的内部地址,也与内部地址不同。

    如何保证哈希值的唯一性

    一、哈希表(散列表)概述

    hashCode()最终返回的是一个int 值,是有范围的,并且不是一个单映射结构。

    哈希表结构:
    对象数组+链表

    存储原理:
    哈希表在存储的时候会调用对象的hashCode值,拿到返回的int值,然后去跟默认数组的长度进行取余运算,得到一个具体的下标,这个下标用来决定这个对象究竟存到什么位置。

    优点:
    这样做的好处是根本不需要对数组进行遍历,通过哈希值取余运算就能很快找到数据存储的位置。

    当取余得到的数据一样时,链表会依次往下存储,当哈希桶中的数据量大于8时,从链表转化为红黑二叉树,会更利于查找。当哈希桶中的数据减少到6时,又会从红黑树转化为链表。

    补充:
    问题:

    已知我的链表哈希桶里现在有7个数据,它一定会从红黑树转为链表吗?

    解答:

    这个题的坑在于已知有7个数据,但是他并没有告诉你这时候是已经转成了红黑树,所以得一分为二的看待。

    如果是一开始存数据,那么现在本身就还是链表,不存在再次转为链表的问题。

    如果是已经转为了红黑树,现在数据减少到7,那么当数据减少到6的时候一定会转化为链表。

    二、解决方法

    1.必须重写hashCode()方法
    解释:
    每new一个对象就会有一个新的地址值,哈希码值几乎都不相等,那么在放进哈希桶中重复的几率就特别低。

    比如:
    new Person(“张三”,23); 0x123
    new Person(“张三”,23); 0x124
    虽然两个对象的值一样,但是,它们两个的地址不一样,所以哈希码值不一样,会放在不同的哈希桶中,不会去重。

    作用:
    1.只有重写了hashCode()方法才能调用equals()方法,因为子类不重写就继承父类,而父类(Object类)就是依据你的地址值进行计算的,但是每new一个对象就会有一个新的地址值,地址值不一样,得到的哈希码值一定不重复,所以一定不会调用equals()方法。因为只有出现同一个索引,才会使用equals()方法去对比。

    2.尽可能少调用equals()方法 如果对象的属性值一样,那么尽可能让他们的哈希码值也一样,那么一定会调用equals()方法,这样就会去重了。
    使用系统默认的hashCode()方法,减少equals()方法调用次数,提高效率。

    2.必须重写equals()方法
    解释:

    equals()用来对比你的属性是否相同。当哈希值相同时调用equals()方法对比属性,如果属性相同,则认为是同一个对象,会去重。

    注意:这两个方法只重写其中一个不去重,必须同时重写!

    三、Java中的哈希表:

    初始桶数量 : 16

    散列因子 : 0.75 (哈希桶的数量是会变化的,当桶中已经有75%的地方存有数据了,那么对桶进行扩容,桶就会变得更多。扩容大小默认为原长度的两倍)

    一、何为加载因子?

    加载因子是表示Hsah表中元素的填满的程度.若:加载因子越大,填满的元素越多,好处是,空间利用率高了,但:冲突的机会加大了.反之,加载因子越小,填满的元素越少,好处是:冲突的机会减小了,但:空间浪费多了.

    冲突的机会越大,则查找的成本越高.反之,查找的成本越小.因而,查找时间就越小.

    因此,须在 "冲突的机会"与"空间利用率"之间寻找一种平衡与折衷. 这种平衡与折衷本质上是数据结构中有名的"时-空"矛盾的平衡与折衷.

    二、HashMap中的加载因子

    HashMap默认的加载因子是0.75,最大容量是16,因此可以得出HashMap的默认容量是:0.75*16=12。

    用户可以自定义最大容量和加载因子。

    HashMap 包含如下几个构造器:

    • HashMap():构建一个初始容量为 16,负载因子为 0.75 的 HashMap。
    • HashMap(int initialCapacity):构建一个初始容量为 initialCapacity,负载因子为 0.75 的 HashMap。
    • HashMap(int initialCapacity, float loadFactor):以指定初始容量、指定的负载因子创建一个 HashMap。
    展开全文
  • JavaHashMap用法

    2020-08-12 10:25:58
    HashMap<String , Integer> map = new HashMap<>(); map.put("a",1);//增 map.remove("a");//删 map.put("a",2);//改 map.containsKey("zuo");//查(是否存在)
    HashMap<String , Integer> map = new HashMap<>();
    		map.put("a",1);//增
    		map.remove("a");//删
    		map.put("a",2);//改
    		map.containsKey("zuo");//查(是否存在)
    
    展开全文
  • JavaHashMap的理解

    2020-12-20 17:06:09
    在实现快速查询上,哈希表是经常使用的一种数据结构,是根据关键码值(Key value)而直接进行访问的数据结构。而Java实现HashMap的原理具体是怎样的,需要以下方面来理解: 1.Java中分散与指针结构 ...

    在实现快速查询上,哈希表是经常使用的一种数据结构,是根据关键码值(Key value)而直接进行访问的数据结构。而Java实现HashMap的原理具体是怎样的,需要以下方面来理解:
    1.Java中分散与指针结构
    2.HashMap的构造函数以及各个版本底层构造
    3.hashCode的计算以及数组下标的计算
    4.哈希冲突的发生和解决方案

    一、Java中分散与指针结构

    Java编程语言实现HashMap时,是采用分散和指针的数据结构实现一个“链表散列”的数据结构,即是和链表的结合体。

    在这里插入图片描述

    二、HashMap的构造函数以及各个版本底层构造

    1.HashMap构造函数为:

    //构造函数1
    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);//新的扩容临界值
    }
    
    //构造函数2
    public HashMap(int initialCapacity) {
        this(initialCapacity, DEFAULT_LOAD_FACTOR);
    }
    
    //构造函数3
    public HashMap() {
        this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
    }
    
    //构造函数4用m的元素初始化散列映射
    public HashMap(Map<!--? extends K, ? extends V--> m) {
        this.loadFactor = DEFAULT_LOAD_FACTOR;
        putMapEntries(m, false);
    }
    

    2.底层构造:
    在JDK1.6、1.7中,HashMap位桶和链表来实现,而在JDK1.8中,则采用数组和红黑树来实现:
    位桶数组:

    //Node是单向链表,它实现了Map.Entry接口
    static class Node<k,v> implements Map.Entry<k,v> {
        final int hash;
        final K key;
        V value;
        Node<k,v> next;
        //构造函数Hash值 键 值 下一个节点
        Node(int hash, K key, V value, Node<k,v> next) {
            this.hash = hash;
            this.key = key;
            this.value = value;
            this.next = next;
        }
    
        public final K getKey()        { return key; }
        public final V getValue()      { return value; }
        public final String toString() { return key + = + value; }
    
        public final int hashCode() {
            return Objects.hashCode(key) ^ Objects.hashCode(value);
        }
    
        public final V setValue(V newValue) {
            V oldValue = value;
            value = newValue;
            return oldValue;
        }
        //判断两个node是否相等,若key和value都相等,返回true。可以与自身比较为true
        public final boolean equals(Object o) {
            if (o == this)
                return true;
            if (o instanceof Map.Entry) {
                Map.Entry<!--?,?--> e = (Map.Entry<!--?,?-->)o;
                if (Objects.equals(key, e.getKey()) &&
                    Objects.equals(value, e.getValue()))
                    return true;
            }
            return false;
        }
    

    红黑树:

    //红黑树
    static final class TreeNode<k,v> extends LinkedHashMap.Entry<k,v> {
        TreeNode<k,v> parent;  // 父节点
        TreeNode<k,v> left; //左子树
        TreeNode<k,v> right;//右子树
        TreeNode<k,v> prev;    // needed to unlink next upon deletion
        boolean red;    //颜色属性
        TreeNode(int hash, K key, V val, Node<k,v> next) {
            super(hash, key, val, next);
        }
    
        //返回当前节点的根节点
        final TreeNode<k,v> root() {
            for (TreeNode<k,v> r = this, p;;) {
                if ((p = r.parent) == null)
                    return r;
                r = p;
            }
        }
    

    三、hashCode的计算以及数组下标的计算

    1.HashCode的计算:
    在java.util.HashMap中默认初始容量必须是2的整次数幂,原因是N为2的整数次幂,更加合理。
    h(hashcode) = hashcode % N 等价于 h(hashcode) = hashcode &(N-1)
    //操作与运算符,两数相与都为1结果才为1.而操作符&的运算速率比%效率更高

    java.lang.Object.hashCode() 的实现:

    public native int hashCode();  
      
    /* 
     
    Returns a hash code value for the object. This method is supported for the benefit 
    of hashtables such as those provided by java.util.Hashtable. 
     
    The general contract of hashCode is: 
     
    1. Whenever it is invoked on the same object more than once during an execution  
       of a Java application, the hashCode method must consistently return the same  
       integer, provided no information used in equals comparisons on the object is  
       modified. This integer need not remain consistent from one execution of an  
       application to another execution of the same application. 
     
    2. If two objects are equal according to the equals(Object) method, then calling  
       the hashCode method on each of the two objects must produce the same integer  
       result. 
     
    3. It is not required that if two objects are unequal according to the  
       equals(java.lang.Object) method, then calling the hashCode method on each of  
       the two objects must produce distinct integer results. However, the programmer 
       should be aware that producing distinct integer results for unequal objects may 
       improve the performance of hashtables. 
     
     
    As much as is reasonably practical, the hashCode method defined by class Object  
    does return distinct integers for distinct objects. (This is typically implemented 
    by converting the internal address of the object into an integer, but this  
    implementation technique is not required by the JavaTM programming language.) 
     
     
    */  
    

    基于上述的要求,该方法的实现是采用把对象的内存地址用 hash 算法得到一个 int 类型的
    数。这样的确做到了使不同的对象具有不同的 hash 值。
    2.数组下标的计算:
    设定一个table下标i=(table.length- 1) & ((h = key.hashCode()) ^ (h >>> 16))
    当我们put的时候,会根据key获取对应的hash值,然后无符号右移16位(>>> 16),在与原本的hash值进行^计算,然后再与table.length-1进行&计算,最终得出需要放入的位置
    比如:key = “165156”,HashMap中table数组的大小为16,套入公式,那么我们就会计算出存放位置的下标为13
    那么此时如果我们要将原HashMap的table数组扩容一倍,那么数组,以及链表中的对象,都需要重新分配位置,具体情况分为三种:
    第一种情况:数组有值,链表无值,这个时候只需要重新计算位置,放进去就OK了(e.hash & (newCap - 1))这个计算方式,就是上文数组下标计算,这里有个骚操作,可以结合第三种情况一起说
    第二种情况:数组有值,链表无值 ,但是红黑树有值,
    第三种情况:数组有值,链表有值,业务处理逻辑:循环列表判断每一个元素是否需要换位置,不需要的重新组合成链表,放入当前数组下标中,如果需要的,组成一个链表,放入指定的数组下标中

    四、哈希冲突的发生及解决方案

    1.哈希冲突的发生:
    哈希冲突的发生,当两个不同的数据通过hash算出来的HashCode的值相同时,两者都要放在hash表的同一个位置中时,就发生了hash冲突。
    2.哈希冲突解决方案:
    在解决哈希冲突方面有多种方法:
    一、 开放定址法
    一旦发生冲突,就去寻找新的空的地址或者找到了相同的给定关键字,若未找到,则会失败。
    二、双哈希法
    使用多个Hash算法,在发生冲突后,使用后面的第二个,第三个…直到无冲突。
    三、 链地址法
    每个哈希表节点都有一个next指针,多个哈希表节点可以用next指针构成一个单向链表,被分配到同一个索引上的多个节点可以用这个单向 链表连接起来。此时即使产生冲突,但是可以通道next指针将冲突所在所在的节点连接起来,这样就解决了哈希的冲突问题。
    四、 建立公共溢出区
    即建立一个缓冲区,将冲突的放在缓冲区中,在查找不到时在缓冲区中查找。

    展开全文
  • JavaHashMap常用方法

    2020-06-10 00:02:10
    class qb { private Integer num; private String type; public String getType() { return type; } public void setType(String type) { this.type = type; } public Integer getNum() { ... .
  • javaHashMap与currentHashMap

    2019-09-19 01:06:13
    JDK1.7中: HashMap: hashMap的实现就是通过数组加链表的形式组成的,初始时的容量为16,0.75*16 通过key对数组的长度进行取模计算,然后将entry挂在数组的位置上。 为什么要扩容: 多个entry在链表的时候...
  • Map这样的KeyValue在软件开发中是非常经典的结构,常用于在内存中存放数据。本篇主要想讨论ConcurrentHashMap这样一个并发容器,在正式开始之前我觉得有必要谈谈HashMap,没有它就不会有后面的ConcurrentHashMap。...
  • java hashmap 源码
  • javaHashMap的hash算法扰动函数

    万次阅读 2017-08-24 17:33:56
    文章借鉴... HashMap的高深有一本分就在于key值的快速散列接下来贴出 1.7 和1.8的代码 1.8源码 static final int hash(Object key) { int h; return (key == null) ? 0 : (h = key.hashCo
  • part 1Java基础核心技术总结 万事都离不开根基,Java基础对于一些老鸟来说可能已经很熟悉了,但是对于很多开发5年以下的朋友掌握的或许不是很牢固,因此这份笔记更适合温故而知新。 Part 2并发编程(架构师筑基...
  • class Solution { public boolean canFormArray(int[] arr, int[][] pieces) { // 构造 HashMap O(1) 取值 Map<Integer, int[]> map = new HashMap<>(); for (int[] piece : pieces) { ...
  • /** * */ package Array; import java.util.HashMap; /** @author sq @date 2020年6月8日 @Desription: TODO */ public class Test { public static void main(String[] ... public static void count(String s
  • java hashmap源码图像追踪器java 用 Java 编写的用于桌面的简单光栅图像跟踪器和矢量化器。 请参阅 Android 版本。 安德拉斯·扬科维奇 这是 imagetracer.js ...查看具有更好颜色量化算法的重构版本: ...1
  • 文章目录1. HashMap 概述2. HashMap 的数据结构2.1 Node结构2.2 HashMap中的哈希与threshold, loadFactor, szie等几个字段3. HashMap功能实现-方法3.1 确定哈希桶数组索引位置3.2 分析HashMap的put方法3.3 扩容机制4...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 873
精华内容 349
关键字:

javahashmap

java 订阅