`
L.G.Alexander
  • 浏览: 155746 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

(十六):Set元素不能重复,重写equals方法就必须重写hashCode方法

阅读更多
public class People{
	String name;
	public People(String name){
		this.name = name;
	}
	public String getName(){
		return name;
	}
}
public class SetTest {
	public static void main(String[] args){
		Set<String> set = new HashSet<String>();//语句(1)
		System.out.println(set.add("abc"));//语句(2)
		System.out.println(set.add("xyz"));//语句(3)
		System.out.println(set.add("abc"));//语句(4)
		for(Iterator<String> it = set.iterator();it.hasNext();){//语句(5)
			System.out.println(it.next());
		}
		
		Set<People> st = new HashSet<People>();//语句(6)
		st.add(new People("zhangsan"));//语句(7)
		st.add(new People("lisi"));//语句(8)
		st.add(new People("zhangsan"));//语句(9)
		for(Iterator<People> it = st.iterator();it.hasNext();){//语句(10)
			System.out.println(it.next().getName());
		}
	}
}

问题1:执行完语句语句(1)(2)(3)(4)(5),打印结果是什么?为什么?
解答:执行完语句语句(1)(2)(3)(4)(5),打印结果是true,true,false,abc,xyz。因为String类重写Object类的equals和hashCode方法。
分析:Set是数学上的集合的集合的概念。关于数学上的集合它是不允许有重复元素的,举例:我有一个集合,元素是1、2、3、4、5,我在往集合里面增加元素3,这个集合的元素还是1、2、3、4、5,不会说是1、2、3、3、4、5。集合里的元素不允许有重复元素。java里面的Set完全反映到数学集合上的意义。java里面的Set里面的元素也是不运行重复的。在上述代码中有两个abc,是重复的。既然是重复的,你添加完一个再去添加另外一个相同的元素,是不能添加进去的。关于Set里面的一些方法,jdk文档是这样定义的:
boolean add(E e)
Adds the specified element to this set if it is not already present (optional operation). More formally, adds the specified element e to this set if the set contains no element e2 such that (e==null ? e2==null : e.equals(e2)). If this set already contains the element, the call leaves the set unchanged and returns false. In combination with the restriction on constructors, this ensures that sets never contain duplicate elements. (翻译:果 set 中尚未存在指定的元素,则添加此元素(可选操作)。更正式地说,如果此 set 没有包含满足下列条件的元素 e,则向 set 中添加指定的元素 o:(o==null ? e==null.equals(e))。如果此 set 已经包含指定的元素,则该调用不改变此 set 并返回 false。结合构造方法上的限制,这就可以确保 set 永远不包含重复的元素。)
The stipulation above does not imply that sets must accept all elements; sets may refuse to add any particular element, including null, and throw an exception, as described in the specification for Collection.add. Individual set implementations should clearly document any restrictions on the elements that they may contain.(翻译:上述规定并未暗示 set 必须接受所有元素;set 可以拒绝添加任意特定的元素,包括 null,并抛出异常,这与 Collection.add 规范中所描述的一样。每个 set 实现应该明确地记录对其可能包含元素的所有限制。)
也就是说,添加一个元素成功,会返回true,否则返回false.

问题2:执行完语句语句(6)(7)(8)(9)(10),打印结果是什么?为什么?
解答:执行完语句语句(6)(7)(8)(9)(10),打印结果是zhangsan,lisi,zhangsan。
分析:People类没有重写Object类的equals和hashCode方法,而Object类的equals方法比较的是对象的内存地址,Object类的hashCode方法返回的也是对象的内存地址,在语句(7)和语句(9)分别在堆里面生成了2个对象,它们的内存地址是不一样的。

问题3:为什么语句(4)中的abc添加不到Set中,而语句(9)中的第二个zhangsan能添加到Set中呢?
解答:学过数据结构就知道Hash是一个人的名字,在数据结构里面有Hash表Hash函数。在java里面对HashSet的实现也是使用Hash的方式去实现的。为什么用Hash的方式去实现呢?可以这样考虑:当如果不用Hash方式实现,还用正常方式实现,集合是不运行元素重复出现的,如果集合里已经有了1000个对象,这是我往集合里面增加第1001个对象,增加之前会把第1001个对象和集合里已经有的1000个对象一一比较,判断是否存在将要增加的对象,如果存在就不增加,如果不存在就增加,这样就存在效率问题。有十万个就要比较十万次,这么低的效率在我们现实开发里面是不允许的。java考虑到效率的问题没有按照正常的方式,而是采用了Hash的方式。HashSet的存储机制:Hash是散列,它是这样比较的,当往集合里面增加一个对象的时候,我们是把对象的引用(对象的内存地址)增加到集合里面去了,当把对象的引用增加到集合里面去后,集合要做什么呢?它首先读取增加对象的hashCode (调用对象的hashCode()方法,hashCode()方法在在Object类里面定义的),根据hashCode,集合去计算存放对象的位置,
当这个位置没有存放对象的话,集合就认为这个对象没有重复,直接就存放这个对象,这是第一种情况。第二种情况,首先它还是要计算hashCode,得到hash码,同样还是要计算位置,当它计算出位置时,发现这个位置已经有这个对象存在了,它并不会立刻告知增加不成功,它会接着调用你增加这个对象的equals方法跟已经在位置上存在的对象进行比较,如果equals方法返回true,表示这两个对象的内容是一样的,既然对象的内容是一样,集合就认为这个对象已经在集合里面存在了,就增加不进去了。集合的add方法返回一个false。如果equals方法返回false,表示将要增加的对象和集合里面已经存在的对象的内容不一样,既然内容不一样,集合就再去散列一次,计算出新的地址,把你这个对象放到新的地址上去,还是会增加成功。

问题4:怎样重写People类的equals和hashCode方法,而使语句(9)中的第二个zhangsan不能添加到Set中呢?
解答:代码如下:
@Override
	public boolean equals(Object obj) {
		if(obj == null) return false;
		if(this == obj) return true;
		if(obj instanceof People)
			if(obj.equals(this))return true;
		return false;
	}
	@Override
	public int hashCode() {
		return name.hashCode();
	}
总结:1、当向集合Set中增加对象时,首先集合计算要增加对象的hashCode码,根据该值来得到一个位置用来存放当前对象,挡在该位置没有一个对象存在的话,那么集合Set认为该对象在集合中不存在,直接增加进去。如果在该位置有一个对象存在的话,接着将准备增加到集合中的对象与该位置上的对象进行equals方法比较,如果该equals方法返回false,那么集合认为集合中不存在该对象,在进行一次散列,将该对象放到散列后计算出的新地址里,如果equals方法返回true,那么集合认为集合中已经存在该对象了,不会再将该对象增加到集合中了。
    2、重写equals方法的时候必须重写hashCode方法。如果一个类的两个对象,使用equals方法比较时,结果为true,那么该两个对象具有相同的hashCode。原因是equals方法为true,表明是同一个对象,它们的hashCode当然相同。
    3、Ojbect类的hashCode方法返回的是Object对象的内存地址。我们可以通过Integer.toHexString(new Object().hashCode);来得到。
   
分享到:
评论

相关推荐

    JAVA集合的使用(List、Map、Set、Queue,Collections、Comparable与Comparator,排序、搜索,内部类、equals、hashCode)

    要注意的是List,Set,Queue继承了Collection接口,...这里想用一个简单的例子展示一下他们的使用,内容包括:List、Map、Set、Queue,Collections、Comparable与Comparator,排序、搜索,内部类,泛型、重写equals、hashCode

    set容器无重复注意问题.png

    凡是以hashcode为存储位置计算的容器都需要重写hashcoe方法,为什么重写类的hashcode和equals方法?如果不重写会有后果?以及set容器的元素储存方式

    kotlin中数据类重写setter getter的正确方法

    equals()/hashCode()函数对, toString() 函数, 输出格式为 “User(name=John, age=42)” , componentN() 函数群, 这些函数与类的属性对应, 函数名中的数字 1 到 N, 与属性的声明顺序一致, copy

    Hibernate注解映射联合主键

    该类需要实现java.io.Serializable接口并重写equals和hascode,再将该类注解为@Embeddable,最后在主类中(该类不包含联合主键类中的字段)保存该联合主键类的一个引用,并生成set和get方法,并将该引用注解为@Id ...

    实验05 Java集合.doc

    注意:因为Person类是自定义类,需要重写hashCode()方法和equals()方法,并规定只有姓名和身份证号都相等,则对象相等。 其中计算哈希码的算法:(31 + ((name == null) ? 0 : name.hashCode()))*31 + id (注:...

    HashTable和HashMap的区别_动力节点Java学院整理

    HashSet实现了Set接口,它不允许集合中有重复的值,当我们提到HashSet时,第一件事情就是在将对象存储在HashSet之前,要先确保对象重写equals()和hashCode()方法,这样才能比较对象的值是否相等,以确保set中没有...

    HashSet和HashMap的区别_动力节点Java学院整理

    HashSet实现了Set接口,它不允许集合中有重复的值,当我们提到HashSet时,第一件事情就是在将对象存储在HashSet之前,要先确保对象重写equals()和hashCode()方法,这样才能比较对象的值是否相等,以确保set中没有...

    Java面试题.docx

    31、HashSet与HashMap怎么判断集合元素重复? 33、ArrayList和LinkedList的区别,以及应用场景 34、数组和链表的区别 35、开启线程的三种方式? 36、线程和进程的区别? 38、run()和start()方法区别 39、如何...

    Hibernate中文API大全

    元素允许你映射一个组件类作为一个Map的key,前提是你必须正确的在这个类中重写了hashCode() 和 equals()方法。 8.4. 组件作为联合标识符(Components as composite identifiers) 你可以使用一个组件作为一个实体类...

    疯狂JAVA讲义

    学生提问:为什么方法的返回值类型不能用于区分重载的方法? 124 5.3 成员变量和局部变量 124 5.3.1 成员变量和局部变量 125 5.3.2 成员变量的初始化和内存中的运行机制 128 5.3.3 局部变量的初始化和内存中的...

    javaSE代码实例

    14.2.2 重写hashCode方法 275 14.3 集合框架的层次结构 -277 14.4 Ordered与Sorted的接口 278 14.4.1 Ordered的排序 278 14.4.2 Sorted的排序 279 14.5 列表 279 14.5.1 列表接口——st 279 14.5.2 ...

    Java基础知识点总结.docx

    equals()方法和hashCode()方法 270 数据结构 273 Array方法类汇总 304 Java数组与集合小结 305 递归 309 对象的序列化 310 Java两种线程类:Thread和Runnable 315 Java锁小结 321 java.util.concurrent.locks包下...

    安卓java读取网页源码-interview:安卓面试

    怎么判断集合元素重复? String、StringBuffer、StringBuilder 之间的区别? 对反射的了解? 对注解的了解? 对依赖注入的了解? 对泛型的了解? 泛型中 extends 和 super 的区别? 对 Java 的异常体系的了解? 对...

    AIC的Java课程1-6章

    第9章 常用类 4课时  理解Object类及其常用方法equals,hashCode和finalize等。  能够使用String,StringBuffer,StringBuilder类创建字符串对象和使用其方法,分辨不同类之间的区别。 ...

    Array_Vitalij_Rusakevic

    类Object重写方法( toString() , equals() , hashCode() )。 不应在此Array类中放置任何逻辑该程序应支持以下操作: 排序数组; 搜索数组项(使用二进制搜索算法); 搜索max (最大值)和min (最小值)数组项...

    EpamArraysTesting

    类Object重写方法( toString() , equals() , hashCode() )。 不应在此Array类中放置任何逻辑该程序应支持以下操作: 排序数组; 搜索数组项(使用二进制搜索算法); 搜索max (最大值)和min (最小值)数组项...

    JAVA面试常见问题整理

    接着,文章详细解释了equals和hashCode的用法及区别,以及String、StringBuffer、StringBuilder的区别和适用场景。 此外,文章还涵盖了Java中的一些基本概念,如final、interface、abstract类、重载和重写等。同时...

    JAVA基础课程讲义

    equals和hashcode方法 143  泛型 144 思考作业 145 上机作业 145 第八章 IO技术 146 为什么需要学习IO技术 146 基本概念 146 数据源 146 流的概念 146 第一个简单的IO流程序及深入(将文件中的数据读入) 146 Java...

Global site tag (gtag.js) - Google Analytics