java包装类的比较、hash和CollectionUtils交集原理探究

转载请注明出处:http://blog.csdn.net/gklifg/article/details/45914169

1.连等(==)比较的适用与不适用场景

场景1:

<pre name="code" class="java">public void testJava(){
	Long longA = new Long(4l);
	Long longB = (longA -2)*2;

	System.out.println("longA="+longA+",hash="+longA.hashCode());
<span>	</span>System.out.println("longB="+longB+",hash="+longB.hashCode());
	System.out.println(longA==longB);
}

结果:

longA=4,hash=4

longB=4,hash=4false

场景2:

public void testJava(){
	Long longA = new Long(4l);
	Long longB = new Long(4l);

	System.out.println("longA="+longA+",hash="+longA.hashCode());
<span style="white-space:pre">	</span>System.out.println("longB="+longB+",hash="+longB.hashCode());
	System.out.println(longA==longB);
}

结果:

longA=4,hash=4

longB=4,hash=4

false

场景3:

public void testJava(){
	Long longA = 4l;
<span style="white-space:pre">	</span>Long longB = 4l;

	System.out.println("longA="+longA+",hash="+longA.hashCode());
<span style="white-space:pre">	</span>System.out.println("longB="+longB+",hash="+longB.hashCode());
	System.out.println(longA==longB);
}

结果:

longA=4,hash=4

longB=4,hash=4

true

场景4:

public void testJava(){
<span style="white-space:pre">	</span>Long longA = Long.parseLong("4");
<span style="white-space:pre">	</span>Long longB = Long.parseLong("4");
<span style="white-space:pre">	</span>
<span style="white-space:pre">	</span>System.out.println("longA="+longA+",hash="+longA.hashCode());
<span style="white-space:pre">	</span>System.out.println("longB="+longB+",hash="+longB.hashCode());
<span style="white-space:pre">	</span>System.out.println(longA==longB);
}

结果:

longA=4,hash=4

longB=4,hash=4

true

场景5:

public void testJava(){
	Long longA = 4l;//jvm自动分配
	Long longB = new Long(4);//手动创建
	Long longC = Long.parseLong("4");//?
	Long longD = (4-2)*2l;//?
	Long longE = (longA + 4)/2;//?
	Long longF = (longB + 8)/3;//?

	System.out.println("A==C:"+(longA==longC));
	System.out.println("A==D:"+(longA==longD));
	System.out.println("B==C:"+(longB==longC));
	System.out.println("B==D:"+(longB==longD));
	System.out.println("C==D:"+(longC==longD));
	System.out.println("A==E:"+(longA==longE));
	System.out.println("B==E:"+(longB==longE));
	System.out.println("A==F:"+(longA==longF));
	System.out.println("B==F:"+(longB==longF));
}

结果:

A==C:true

A==D:true

B==C:false

B==D:false

C==D:true

A==E:true

B==E:false

A==F:true

B==F:false

结论:可以看出:只要不是手动new出来的Long型值其他途径,包括parseLong方法,产生的对象都是jvm自动分配的对象。

2.HashSet的去重机制

HashSet实际上是HashMap的封装,所以去重机制继承HashMap的做法,而HashMap的hash函数依赖于Ojbect.hashCode(),从上面的例子可以知道,同值的包装类的hashCode是相同的,所以所有同值的Long都会被去重。Set中先被放入的对象留下,其他的都丢弃。如果吧A、B两行顺序调换,那么Set中留下的就是B了。

public void testJava(){
	Long longA = 4l;//jvm自动分配
	Long longB = new Long(4);//手动创建
	Long longC = Long.parseLong("4");//?
	Long longD = (4-2)*2l;//?
	Long longE = (longA + 4)/2;//?
	Long longF = (longB + 8)/3;//?

	Set<Long> longSet = new HashSet<Long>();
	longSet.add(longA);//注意这两行
	longSet.add(longB);<span style="font-family: Arial, Helvetica, sans-serif;">//注意这两行</span>
	longSet.add(longC);
	longSet.add(longD);
	longSet.add(longE);
	longSet.add(longF);

	System.out.println(longSet.size());

	for(Long l:longSet){
		System.out.println("A==l:"+(longA==l));
		System.out.println("B==l:"+(longB==l));
	}

}

结果:

1

A==l:true

B==l:false

3.CollectionUtils.intersection()的去重规则:

源码如下:

 public static Collection intersection(final Collection a, final Collection b) {
        ArrayList list = new ArrayList();
        Map mapa = getCardinalityMap(a);//key为a的元素,value为元素出现次数,下同
        Map mapb = getCardinalityMap(b);
        Set elts = new HashSet(a);
        elts.addAll(b);<span style="font-family: Arial, Helvetica, sans-serif;">//元素经hash去重后的并集</span>

        Iterator it = elts.iterator();
        while(it.hasNext()) {
            Object obj = it.next();
<span style="white-space:pre">	</span>    //对于每个元素,如果a或b没有次元素,那么跳过;如果都有若干个,那么放入“个数较小一方”个该元素
<span style="white-space:pre">	</span>    //可以看到,<span style="font-family: Arial, Helvetica, sans-serif;">不管做交集之前有多少个相同对象,只要他们hash一致,</span>放入结果集的都是同一个对象
            for(int i=0,m=Math.min(getFreq(obj,mapa),getFreq(obj,mapb));i<m;i++) {
                list.add(obj);
            }
        }
        return list;
    }

源码中可以看到apache的collectionUtils.intersection()也是依赖于hashCode的(mapa、mapb都是HashMap),所以基本类的包装类可以放心的去重,不用担心因对象地址不同而没有去重的问题。

时间: 2024-10-29 16:40:42

java包装类的比较、hash和CollectionUtils交集原理探究的相关文章

Java包装类的常量池

Integer a=Integer.valueOf(100); Integer b=Integer.valueOf(100); System.out.println(a==b); Double d1=Double.valueOf(100); Double d2=Double.valueOf(100); System.out.println(d1==d2); 为什么包装类 Ingeter两个值就相等 Double的就不相等了呢 在给Integer赋值时,实际上是自动装箱的过程,也就是调用了Inte

【源码】java包装类总结

1.包装类除了Void和Character,其他六个全部都继承自Number.Number是一个抽象类.如下: public abstract class Number implements java.io.Serializable { public abstract int intValue(); public abstract long longValue(); public abstract float floatValue(); public abstract double double

Java包装类的介绍与应用

Java包装类的介绍: 在Java的设计中提倡一种思想,就是一切皆为对象.然而我们知道,Java的数据类型分为基本数据类型和引用数据类型,但基本数据怎么能够成为对象呢?为了解决这个问题,就需要把8种基本类型包装成一个类的形式,这就是包装类的作用. 装箱与拆箱: public class WrapperDemo01{ public static void main(String args[]){ int x = 30 ; // 基本数据类型 Integer i = new Integer(x) ;

java包装类的缓存机制(转)

出处: java包装类的缓存机制 java 包装类的缓存机制,是在Java 5中引入的一个有助于节省内存.提高性能的功能,只有在自动装箱时有效 Integer包装类 举个栗子: Integer a = 127; Integer b = 127; System.out.println(a == b); // true 这段代码输出的结果为true 使用自动装箱将基本类型转为封装类对象这个过程其实底层实现是调用封装类的valueOf方法: Integer a =127; 相当于 Integer a

深入Java集合学习系列:HashMap的实现原理

参考文献 引用文献:深入Java集合学习系列:HashMap的实现原理,大部分参考这篇博客,只对其中进行稍微修改 自己曾经写过的:Hashmap实现原理 1. HashMap概述: HashMap是基于哈希表的Map接口的非同步实现(Hashtable跟HashMap很像,唯一的区别是Hashtalbe中的方法是线程安全的,也就是同步的).此实现提供所有可选的映射操作,并允许使用null值和null键.此类不保证映射的顺序,特别是它不保证该顺序恒久不变. 2. HashMap的数据结构: 在ja

深入Java集合学习系列:LinkedHashMap的实现原理

1. LinkedHashMap概述: LinkedHashMap是Map接口的哈希表和链接列表实现,具有可预知的迭代顺序.此实现提供所有可选的映射操作,并允许使用null值和null键.此类不保证映射的顺序,特别是它不保证该顺序恒久不变.   LinkedHashMap实现与HashMap的不同之处在于,后者维护着一个运行于所有条目的双重链接列表.此链接列表定义了迭代顺序,该迭代顺序可以是插入顺序或者是访问顺序.   注意,此实现不是同步的.如果多个线程同时访问链接的哈希映射,而其中至少一个线

优秀Java程序员必须了解的GC工作原理

一个优秀的Java程序员必须了解GC的工作原理.如何优化GC的性能.如何与GC进行有限的交互,因为有一些应用程序对性能要求较高,例如嵌入式系统.实时系统等,只有全面提升内存的管理效率 ,才能提高整个应用程序的性能. 一个优秀的Java程序员必须了解GC的工作原理.如何优化GC的性能.如何与GC进行有限的交互,因为有一些应用程序对性能要求较高,例如嵌入式系统.实时系统等,只有全面提升内存的管理效率 ,才能提高整个应用程序的性能.本篇文章首先简单介绍GC的工作原理之后,然后再对GC的几个关键问题进行

【转】优秀的Java程序员必须了解GC的工作原理

一个优秀的Java程序员必须了解GC的工作原理.如何优化GC的性能.如何与GC进行有限的交互,因为有一些应用程序对性能要求较高,例如嵌入式系统.实时系统等,只有全面提升内存的管理效率 ,才能提高整个应用程序的性能.一个优秀的Java程序员必须了解GC的工作原理.如何优化GC的性能.如何与GC进行有限的交互,因为有一些应用程序对性能要求较高,例如嵌入式系统.实时系统等,只有全面提升内存的管理效率 ,才能提高整个应用程序的性能.本篇文章首先简单介绍GC的工作原理之后,然后再对GC的几个关键问题进行深

Java线程池使用和分析(二) - execute()原理

相关文章目录: Java线程池使用和分析(一) Java线程池使用和分析(二) - execute()原理 execute()是 java.util.concurrent.Executor接口中唯一的方法,JDK注释中的描述是“在未来的某一时刻执行命令command”,即向线程池中提交任务,在未来某个时刻执行,提交的任务必须实现Runnable接口,该提交方式不能获取返回值.下面是对execute()方法内部原理的分析,分析前先简单介绍线程池有哪些状态,在一系列执行过程中涉及线程池状态相关的判断