精华内容
下载资源
问答
  • hashCode 原理

    2021-02-25 20:20:31
    网上一般都说hashCode 就是对象的内存地址,但是你想下垃圾回收时(复制算法,整理算法)都要发生对象移动,都要改变对象的内存地址。但hashCode又不能变化,那么该值一定是被保存在对象的某个地方了。 hashcode就是...

    网上一般都说hashCode 就是对象的内存地址,但是你想下垃圾回收时(复制算法,整理算法)都要发生对象移动,都要改变对象的内存地址。但hashCode又不能变化,那么该值一定是被保存在对象的某个地方了。

    hashcode就是保存在对象头里面的,但是如果hashcode是内存地址的话,那么就有可能会重复到之前回收对象的地址。对象头详解见此文章

    synchronized 原理 锁升级 详解

    苍天可见,hashcode绝对不是简简单单的内存地址。

    hashcode的6种生成策略

    可以通过在JVM启动参数中添加-XX:hashCode=4,改变默认的hashCode计算方式。

    1. hashCode=0

    if (hashCode == 0) {
         value = os::random() ;
      }
    

    此类方案返回一个Park-Miller伪随机数生成器生成的随机数OpenJdk 6 &7的默认实现。

    2. hashCode=1

    if (hashCode == 1) {
         intptr_t addrBits = cast_from_oop<intptr_t>(obj) >> 3 ;
         value = addrBits ^ (addrBits >> 5) ^ GVars.stwRandom ;
    }
    

    此类方案将对象的内存地址,做移位运算后与一个随机数进行异或得到结果。

    3. hashCode = 2

    if (hashCode == 2) {
         value = 1 ;            // for sensitivity testing
    }
    

    此类方案返回固定的1。

    4. hashCode = 3

    if (hashCode == 3) {
         value = ++GVars.hcSequence ;
    }
    

    此类方案返回一个自增序列的当前值。

    5. hashCode = 4

    if (hashCode == 4) {
         value = cast_from_oop<intptr_t>(obj) ;
    }
    

    此类方案返回当前对象的内存地址。

    6. hashCode 为 其它

    通过和当前线程有关的一个随机数+三个确定值,运用Marsaglia’s xorshift scheme随机数算法得到的一个随机数。JDK8 的默认hashCode的计算方法就是这个xorshift 算法。

    测试hashcode是不是内存地址

    首先有个工具类:利用Unsafe类 打印真实内存地址

    public static class Memory {
            static final Unsafe unsafe = getUnsafe();
            static final boolean is64bit = true;
    
            public static void printAddresses(Object... objects) {
                long last = 0;
                int offset = unsafe.arrayBaseOffset(objects.getClass());
                int scale = unsafe.arrayIndexScale(objects.getClass());
                switch (scale) {
                    case 4:
                        long factor = is64bit ? 8 : 1;
                        final long i1 = (unsafe.getInt(objects, offset) & 0xFFFFFFFFL) * factor;
                        System.out.print(Long.toHexString(i1));
                        last = i1;
                        for (int i = 1; i < objects.length; i++) {
                            final long i2 = (unsafe.getInt(objects, offset + i * 4) & 0xFFFFFFFFL) * factor;
                            if (i2 > last)
                                System.out.print(", +" + Long.toHexString(i2 - last));
                            else
                                System.out.print(", -" + Long.toHexString(last - i2));
                            last = i2;
                        }
                        break;
                    case 8:
                        throw new AssertionError("Not supported");
                }
                System.out.println();
    
            }
    
            private static Unsafe getUnsafe() {
    
                try {
                    Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
                    theUnsafe.setAccessible(true);
                    return (Unsafe)theUnsafe.get(null);
                } catch (Exception e) {
                    throw new AssertionError(e);
                }
    
            }
        }
    

    main方法

     public static void main(String[] args) throws Exception {
            Object o = new Object();
            
            Memory.printAddresses(o);
            System.err.println(Long.toHexString(o.hashCode()));
            System.err.println(Long.toHexString(System.identityHashCode(o)));
        }
    

    打印结果

    76b8b4c28
    4e25154f
    4e25154f
    

    hashCode 跟 System.identityHashCode 方法一样,但是跟内存地址没半毛钱关系。

    我们在改变main方法

     public static void main(String[] args) throws Exception {
            Object o1 = new Object();
            Object o2 = new Object();
            
            Memory.printAddresses(o2);
            System.err.println(Long.toHexString(o2.hashCode()));
            System.err.println(Long.toHexString(System.identityHashCode(o2)));
        }
    

    打印结果

    76b8b4c58
    4e25154f
    4e25154f
    

    很奇怪,我们定义了两个对象,打印第二个对象内存地址和第一次打印的不同,这很正常。

    但是打印hashcode确实跟第一次完全一样,由此可见hashcode值跟对象没关系,可能就是分配好的数列,你构造了一个对象就顺序取取数列中的数作为hashcode。

    hashcode什么时候生成,在哪分配给对象的 还请大神知道的留言给我!!!!谢谢~~

    再来欣赏几个面试题

    1、equals()既然已经能实现对比的功能了,为什么还要hashCode()呢?

    因为重写的equals()里一般比较的比较全面比较复杂,这样效率就比较低,而利用hashCode()进行对比,则只要生成一个hash值进行比较就可以了,效率很高。

    2、hashCode()既然效率这么高为什么还要equals()呢?

    因为hashCode()并不是完全可靠,有时候不同的对象他们生成的hashcode也会一样(生成hash值得公式可能存在的问题),所以hashCode()只能说是大部分时候可靠,并不是绝对可靠,所以我们可以得出:

    equals()相等的两个对象他们的hashCode()肯定相等,也就是用equals()对比是绝对可靠的。

    hashCode()相等的两个对象他们的equals()不一定相等,也就是hashCode()不是绝对可靠的。

    3、为什么要重写 hashcode 和 equals 方法?

    因为原生的hashcode和equals 只是比较类似内存地址的唯一值,也就是说必须是new出的同个对象才返回相等,跟对象里的业务值没关系。 但是有的场景比如订单对象,很明显当订单号相同的两个订单对象就应该是相等的,于是我们需要重写hashcode 和equals 只判断订单号相等则对象相等。

    Hashcode的位移优化

    一些位运算符介绍

    << : 左移运算符,num << 1,相当于num乘以2 低位补0>> : 右移运算符,num >> 1,相当于num除以2 高位补0>>> : 无符号右移,忽略符号位,空位都以0补齐。
    
    
    % : 模运算 取余。
    
    
    ^ : 位异或 第一个操作数的的第n位于第二个操作数的第n位相反,那么结果的第n为也为1,否则为0& : 与运算 第一个操作数的的第n位于第二个操作数的第n位如果都是1,那么结果的第n为也为1,否则为0| : 或运算 第一个操作数的的第n位于第二个操作数的第n位 只要有一个是1,那么结果的第n为也为1,否则为0~ : 非运算 操作数的第n位为1,那么结果的第n位为0,反之,也就是取反运算(一元操作符:只操作一个数)。
    

    String类的hashcode方法

    public int hashCode() {
            int h = hash;
            if (h == 0 && value.length > 0) {
                 // 保证只计算一次,之后直接取hash 这个缓存
                char val[] = value;
    
                for (int i = 0; i < value.length; i++) {
                    h = 31 * h + val[i];
                }
                hash = h;
            }
            return h;
        }
    

    31*h可以被编译器优化为 h左移5位后减h , 因为31 二进制是 11111, 有较高的性能。

    之所以是31原因就是,选太小的质数移位后也是区间很小,hash冲突高,如果选择很大的质数,移位后会导致区间数很大,超出int的范围,而String的hash就是用int存储的。

    其它的原因我也讲不出来,还是请各位看下大佬的解释吧,也请大佬跟我留言解释下为什么就是31,感谢大佬

    强烈推荐一套Java进阶博客,都是干货,走向架构师不是梦!

    Java进阶全套博客

    展开全文
  • HashCode原理

    千次阅读 2018-10-31 07:56:58
    HashCode原理 原理 概念: 根据一定的规则将与对象相关的信息(比如对象的存储地址,对象的字段等)映射成一个数值,这个数值称作为散列值。 作用: 主要是保证基于散列的集合,如HashSet、HashMap以及...

    HashCode原理

    1. 原理
      1. 概念:

    根据一定的规则将与对象相关的信息(比如对象的存储地址,对象的字段等)映射成一个数值,这个数值称作为散列值。

      1. 作用:

    主要是保证基于散列的集合,如HashSet、HashMap以及HashTable等,在插入元素时保证元素不可重复,同时为了提高元素的插入删除便利效率而设计;主要是为了查找的便捷性而存在。

      1. 常见类别
    • Integer.hashCode()即对应的int值;故equals相等则相等。
    • String.equals()是根据具体的内容(实际的字符串),通过特定的算法计算出来的
    • Object.hashCode()是通过对应的地址计算。
    1. equals方法和hashCode方法
      1. 在重写equals方法的同时,必须重写hashCode方法
      2. 设计hashCode()原则

    无论何时,对同一个对象调用hashCode()都应该产生同样的值。

    如果两个对象根据equals方法比较是相等的,那么两个对象的hashCode方法返回的整数值必须相等。

    如果两个对象根据equals方法比较是不等的,则hashCode方法不一定得返回不同的整数。

      1. 应用:哈希表

    可以提供快速的插入操作和查找操作。数据的插入删除便利时间复杂度为O(1)

    1. 使用问题
      1. 哈希表中只覆盖equals()情况下,造成元素的重复性和本身设置的不符。

    如在散列表中,当为了使满足某种条件的两个对象相等(例在属性相等则忽略地址均认为属于同一元素)情况下,重写了equsals()方法,而未重写hashCode()方法。但是散列表判断元素是否相同,是先通过hashCode一致后才equals()比较,则会出现bug。即要想保证元素的唯一性,必须同时覆盖hashCode和equals才行。

      1. 内存泄露:

    在哈希表中元素添加之后,不能修改其与hashCode有关的信息(如用户自定义的hashCode()方法,该方法的值和具体的属性有关),防止remove等操作因元素桶的变化而失效的同时,在集合中进行大量的元素添加后修改值再进行remove移除,导致内存泄露。

    qq_38331606
    1. 总结:
    • Hashcode是为了提高元素在哈希表中的插入删除便利效率而设计的
    • 为了保证程序的严谨性,必须同时覆盖hashCode和equals
    • 在哈希表中元素添加之后,不能修改其与hashCode有关的信息,防止remove操作失效,发生内存泄露
    • 如果两个对象根据equals方法比较是相等的,那么两个对象的hashCode方法返回的整数值必须相等。
    1. 哈希算法

    是将数据依特定算法直接指定到一个地址上。

    这样一来,当集合要添加新的元素时,先调用这个元素的HashCode方法,就一下子能定位到它应该放置的物理位置上。

    (1)如果这个位置上没有元素,它就可以直接存储在这个位置上,不用再进行任何比较了;

    (2)如果这个位置上已经有元素了,就调用它的equals方法与新元素进行比较,相同的话就不存了;

    (3)不相同的话,也就是发生了Hash key相同导致冲突的情况,那么就在这个Hash key的地方产生一个链表,将所有产生相同HashCode的对象放到这个单链表上去,串在一起(很少出现)。这样一来实际调用equals方法的次数就大大降低了,几乎只需要一两次。 

     

     

    展开全文
  • HashCode原理解析

    2017-09-14 14:53:54
    以下内容完全复制博客...(1)HashCode的存在主要是用于查找的快捷性,如Hashtable,HashMap等,HashCode是用来在散列存储结构中确定对象的存储地址的; (2)如果两个对象相同, equals方法一定

    以下内容完全复制博客http://blog.csdn.net/baidu_31657889/article/details/52298367,内容讲的很好,转载

    HashCode定义

     

    (1)HashCode的存在主要是用于查找的快捷性,如Hashtable,HashMap等,HashCode是用来在散列存储结构中确定对象的存储地址的;

    (2)如果两个对象相同, equals方法一定返回true,并且这两个对象的HashCode一定相同;

    (3)如果对象的equals方法被重写,那么对象的HashCode也尽量重写,并且产生HashCode使用的对象,一定要和equals方法中使用的一致,否则就会违反上面提到的第2点;

    (4)两个对象的HashCode相同,并不一定表示两个对象就相同,也就是equals方法不一定返回true,只能够说明这两个对象在散列存储结构中,如Hashtable,他们存放在同一个篮子里。

     

    HashCode作用

     

    Java中的集合(Collection)有两类,一类是List,再有一类是Set。前者集合内的元素是有序的,元素可以重复;后者元素无序,但元素不可重复。 equals方法可用于保证元素不重复,但是,如果每增加一个元素就检查一次,如果集合中现在已经有1000个元素,那么第1001个元素加入集合时,就要调用1000次equals方法。这显然会大大降低效率。 

    于是,Java采用了哈希表的原理

     

    哈希算法也称为散列算法,是将数据依特定算法直接指定到一个地址上。

    这样一来,当集合要添加新的元素时,先调用这个元素的HashCode方法,就一下子能定位到它应该放置的物理位置上。

    (1)如果这个位置上没有元素,它就可以直接存储在这个位置上,不用再进行任何比较了;

    (2)如果这个位置上已经有元素了,就调用它的equals方法与新元素进行比较,相同的话就不存了;

    (3)不相同的话,也就是发生了Hash key相同导致冲突的情况,那么就在这个Hash key的地方产生一个链表,将所有产生相同HashCode的对象放到这个单链表上去,串在一起(很少出现)。这样一来实际调用equals方法的次数就大大降低了,几乎只需要一两次。 

     

    如何理解HashCode的作用:

    从Object角度看,JVM每new一个Object,它都会将这个Object丢到一个Hash表中去,这样的话,下次做Object的比较或者取这个对象的时候(读取过程),它会根据对象的HashCode再从Hash表中取这个对象。这样做的目的是提高取对象的效率。若HashCode相同再去调用equal。

    HashCode实践

     

    HashCode是用于查找使用的,而equals是用于比较两个对象的是否相等的。

    (1)例如内存中有这样的位置 :

    0  1  2  3  4  5  6  7    

    而我有个类,这个类有个字段叫ID,我要把这个类存放在以上8个位置之一,如果不用HashCode而任意存放,那么当查找时就需要到这八个位置里挨个去找,或者用二分法一类的算法。  

    但如果用HashCode那就会使效率提高很多。  定义我们的HashCode为ID%8,比如我们的ID为9,9除8的余数为1,那么我们就把该类存在1这个位置,如果ID是13,求得的余数是5,那么我们就把该类放在5这个位置。依此类推。  

    (2)但是如果两个类有相同的HashCode,例如9除以8和17除以8的余数都是1,也就是说,我们先通过 HashCode来判断两个类是否存放某个桶里,但这个桶里可能有很多类,那么我们就需要再通过 equals 在这个桶里找到我们要的类。  


    请看下面这个例子 

    [java] view plain copy
    1. public class HashTest {    
    2.     private int i;    
    3.     
    4.     public int getI() {    
    5.         return i;    
    6.     }    
    7.     
    8.     public void setI(int i) {    
    9.         this.i = i;    
    10.     }    
    11.     
    12.     public int hashCode() {    
    13.         return i % 10;    
    14.     }    
    15.     
    16.     public final static void main(String[] args) {    
    17.         HashTest a = new HashTest();    
    18.         HashTest b = new HashTest();    
    19.         a.setI(1);    
    20.         b.setI(1);    
    21.         Set<HashTest> set = new HashSet<HashTest>();    
    22.         set.add(a);    
    23.         set.add(b);    
    24.         System.out.println(a.hashCode() == b.hashCode());    
    25.         System.out.println(a.equals(b));    
    26.         System.out.println(set);    
    27.     }    
    28. }    

    输出结果为:

    [java] view plain copy
    1. <pre name="code" class="java">true  
    2. false  
    3. [HashTest@1, HashTest@1] <span style="font-family: Arial, Helvetica, sans-serif;">  </span>  

    以上这个示例,我们只是重写了HashCode方法,从上面的结果可以看出,虽然两个对象的HashCode相等,但是实际上两个对象并不是相等,因为我们没有重写equals方法,那么就会调用Object默认的equals方法,显示这是两个不同的对象。

    这里我们将生成的对象放到了HashSet中,而HashSet中只能够存放唯一的对象,也就是相同的(适用于equals方法)的对象只会存放一个,但是这里实际上是两个对象a,b都被放到了HashSet中,这样HashSet就失去了他本身的意义了。

    下面我们继续重写equals方法:

    [java] view plain copy
    1. public class HashTest {    
    2.     private int i;    
    3.     
    4.     public int getI() {    
    5.         return i;    
    6.     }    
    7.     
    8.     public void setI(int i) {    
    9.         this.i = i;    
    10.     }    
    11.     
    12.     public boolean equals(Object object) {    
    13.         if (object == null) {    
    14.             return false;    
    15.         }    
    16.         if (object == this) {    
    17.             return true;    
    18.         }    
    19.         if (!(object instanceof HashTest)) {    
    20.             return false;    
    21.         }    
    22.         HashTest other = (HashTest) object;    
    23.         if (other.getI() == this.getI()) {    
    24.             return true;    
    25.         }    
    26.         return false;    
    27.     }  
    28.     
    29.     public int hashCode() {    
    30.         return i % 10;    
    31.     }    
    32.     
    33.     public final static void main(String[] args) {    
    34.         HashTest a = new HashTest();    
    35.         HashTest b = new HashTest();    
    36.         a.setI(1);    
    37.         b.setI(1);    
    38.         Set<HashTest> set = new HashSet<HashTest>();    
    39.         set.add(a);    
    40.         set.add(b);    
    41.         System.out.println(a.hashCode() == b.hashCode());    
    42.         System.out.println(a.equals(b));    
    43.         System.out.println(set);    
    44.     }    
    45. }    

    输出结果如下所示。

    从结果我们可以看出,现在两个对象就完全相等了,HashSet中也只存放了一份对象。

    [java] view plain copy
    1. true  
    2. true  
    3. [HashTest@1
    展开全文
  • 目录hashcode原理比较器ComparatorComparable聚合操作 hashcode原理 List的查找效率低下 ...保存数据:在对应的hashcode上放对象,如果该hashcode已经存在对象,那么就在该位置创建一个链表,接


    hashcode原理

    • List的查找效率低下
      HashMap的查找效率好,几乎不花时间。类似平时查字典,先找目录找到多少页,然后直接翻到对应的页码就好了,而这个页码就是HashMap中的hashcode

    • HashMap性能卓越的原因

      • hashcode概念:所有的对象都会有一个对应的hashcode(散列值)
      • 保存数据:在对应的hashcode上放对象,如果该hashcode已经存在对象,那么就在该位置创建一个链表,接着存放对象。
      • 查找数据:根据key计算对应的hashcode,根据hashcode定位查找数组中的值,如果该hashcode找到两个值,那么使用equal进行比对。
    • HashSet判断是否重复
      如果hashcode不同,则在不同的坑里,一定是不同的。
      如果hashcode相同,在相同的坑里,使用equal进行比较

    比较器

    Comparator

    在使用Collections.sort()时,无法确定使用什么属性进行比较,所以我们必须引入Comparator给定对象如何进行大小比较。需要重定义Comparator类中的compare函数

    Comparable

    使对象实现Comparable接口,重定义compareTo方法,这样就不需要额外提供比较器Comparator

    实例

    package List;
    
    import java.util.ArrayList;
    import java.util.Collections;
    import java.util.Comparator;
    import java.util.List;
    
    /**
     * 比较器的使用
     * Comparator
     * Comparable
     *
     *
     * @author Aike
     * @create 2021-05-14 10:36
     */
    
    //实现Comparable接口
    class hero implements Comparable{
        public String name;
        public double hp;
    
        public hero() {
        }
    
        public hero(String name, double hp) {
            this.name = name;
            this.hp = hp;
        }
        public String toString(){
            return "Hero [name="+name+",hp="+hp+"]";
        }
    
        @Override
        public int compareTo(Object h) {
            if(hp>((hero) h).hp)
                return 1;
            else
                return -1;
        }
    }
    public class Comparator1 {
        public static void main(String[] args) {
            List<hero> heros=new ArrayList<hero>();
            heros.add(new hero("后裔",10));
            heros.add(new hero("典韦",8));
            heros.add(new hero("雅典娜",30));
            System.out.println(heros);
    
    //        引入Comparator,定义比较的算法
            Comparator<hero> c=new Comparator<hero>() {
                @Override
                public int compare(hero h1,hero h2) {
                    if(h1.hp> h2.hp)
                        return 1;
                    else
                        return -1;
                }
            };
    
    //        Collections.sort(heros,c);
    //        System.out.println(heros);
    
            Collections.sort(heros);
            System.out.println(heros);
        }
    }
    
    
    [Hero [name=后裔,hp=10.0], Hero [name=典韦,hp=8.0], Hero [name=雅典娜,hp=30.0]]
    [Hero [name=典韦,hp=8.0], Hero [name=后裔,hp=10.0], Hero [name=雅典娜,hp=30.0]]
    

    聚合操作

    JDK8之后,引入了聚合操作,这样就非常好遍历,筛选,比较集合中的元素了
    想要学好聚合操作,必须先掌握Lambda表达式,详见Lambda与聚合操作

    展开全文
  • Java hashcode原理 步骤 1 : List查找低效率 假设在List中存放着无重复名称,没有顺序2000000个Hero 要把名字叫做“hero 1000000”的对象找出来 List做法是对每一个进行挨个遍历,直到找到名字叫做“hero ...
  • Java面试——HashCode的作用原理和实例解析

    万次阅读 多人点赞 2016-08-02 14:50:19
    1. HashCode的特性 (1)HashCode的存在主要是用于查找的快捷性,如Hashtable,HashMap等,HashCode经常用于...(3)两个对象的HashCode相同,并不一定表示两个对象就相同,即equals()不一定为true,只能说明这两...
  • hashCode原理浅议

    千次阅读 2009-03-16 23:21:00
    在hashSet和hashMap中,当添加和查找一个对象时,首先由hashCode()产生一个“散列码”,它是一个int型数值,通过该索引找到数组中对应值,而数组中存储又是外部列表应用,找到“散列码”对应列表后就可以将...
  • (2)如果两个对象相同, equals方法一定返回true,并且这两个对象的HashCode一定相同; (3)两个对象的HashCode相同,并不一定表示两个对象就相同,即equals()不一定为true,只能说明这两个对象在一个散列存储...
  • HashCode重写原理

    2018-05-21 15:49:00
    简介编辑在Java中,哈希码代表对象的特征。例如对象 String str1 = “aa”, str1.hashCode= 3104String str2 = “bb”, str2.hashCode= 3106String str3 = “aa”, str3.hashCode= 3104根据HashCode由此可得出str1!=...
  • 不被重写(原生Object)的hashCode和equals是什么样的? 不被重写(原生)的hashCode值是根据内存地址换算出来的一个值。 不被重写(原生)的equals方法是严格判断一个对象是否相等的方法(object1 == object2)。...
  • 我们也知道在比较一个类是否相同时往往会重写equals方法,值得注意的是,重写equals方法的同时必须也要重写hashCode方法,多次调用一个对象的hashCode方法必须返回同一个数字,这也是必须遵守的规范,不然会造必须...
  • 要把名字叫做“hero 1000000”的对象找出来 List做法是对每一个进行挨个遍历,直到找到名字叫做“hero 1000000”英雄。 最差情况下,需要遍历和比较2000000次,才能找到对应英雄。 测试逻辑: 1. 初始化...
  • 首先再次强调hashcode (==)和equals真正含义(我记得以前有人会说,equals是判断对象内容,hashcode是判断是否相等之类): equals:是否同一个对象实例。注意,是“实例”。比如String s = new String("test...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 1,006
精华内容 402
关键字:

对象的hashcode原理