下面是代码
-------------------------------------------------------------------------------------------------------------------------------------------
package com.kk.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
public class HashSetTest {
public static void main(String[] args) {
Set hashSet=new HashSet();
hashSet.add(new Student(0,"kk"));
hashSet.add(new Student(1,"mm"));
hashSet.add(new Student(1,"mm"));//内存地址和值一样,Set将不会添加同样的对象
Iterator it=hashSet.iterator();
while(it.hasNext()){
Student stu=(Student) it.next();
System.out.println(stu.name);
}
}
static class Student {
int num;
String name;
public Student(int num, String name) {
this.num = num;
this.name = name;
}
@Override
/**
* 判断对象的内存地址是否一样
*/
public int hashCode() {
return num*name.hashCode();
}
@Override
/**
* 判断对象的值是否一样
*/
public boolean equals(Object obj) {
Student stu=(Student) obj;
return this.num==stu.num && this.name.equals(stu.name);
}
}
}
7 个解决方案
#1
HashSet在你调用add的时候,就会调用添加的对象的hashCode方法,而equals方法是在你某些操作的时候,比如remove,HashSet来判断到底是哪一个对象所调用的方法
#2
你去看JDK的源码就知道了!
#3
看看源码就什么都知道了。
#4
private transient HashMap<E,Object> map;
// Dummy value to associate with an Object in the backing Map
private static final Object PRESENT = new Object();
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
这是 hashset 代码中的一部分,hashset 底层是用hashmap来存储的,hashset 存储的值 存放到map中的key,value值 就是PRESENT(一个模拟值而已)。这你就该明白了吧,hashMap的 key 是不能,也不会重复的,hashmap的源码是怎么实现 key不能重复的机制,就是 set集合不能重复的实现。
#5
简单点给你说,add的时候调用equals,在你查找的时候调用hashcode,所以hashcode写得好坏会影响查找效率。
#6
下面是我自己的代码练习和理解,不知道是不是你需要的
===================================================================================
package com.test.example;
import java.util.HashSet;
class Student /*extends Object*/{ //Object是所有自定义类的父类
//hashCode和equals方法是Object中已定义的方法,所有类都有这两个方法
int Sno;
String Sname = null;
public Student(int Sno, String Sname){
this.Sno = Sno;
this.Sname = Sname;
}
//hashCode先被调用,如果在集合中有与当前元素一致的hashCode,调用equals进一步比对
//1.需要重写一个方法hashCode(哈希码)
public int hashCode(){
System.out.println("生成hashCode"); //添加元素时自动调用hashCode
return Sno;
}
//2.重写equals方法
public boolean equals(Object obj){
Student s = (Student)obj;
System.out.println("equals被调用 [" + Sname + "] -- [" + s.Sname + "] ");
boolean b = Sno == s.Sno;
//boolean b = Sname.equals(s.Sname);
return b; //返回true表示一致
}
}
public class StudentSet {
public static void main(String[] args){
/**当使用HashSet存储自定义类时,需要在自定义类中重写equals和hashCode方法,
* 主要原因是集合内不允许有重复的数据元素,在集合校验元素的有效性时(数据元素不可重复),
* 需要调用equals和hashCode验证。
* */
HashSet<Student> stuSet = new HashSet<Student>();
Student s1 = new Student(1, "Lily");
Student s2 = new Student(2, "Tom");
Student s3 = new Student(3, "Jay");
Student s4 = new Student(3, "Jay");
/*stuSet.add(s1);
stuSet.add(s2);
stuSet.add(s3);
stuSet.add(s4);
//System.out.println(stuSet);
//因为没有重写toString方法,所以返回Object默认toString,显示的是元素的位置
System.out.println("stuSet的大小是:" + stuSet.size());//重写之前,stuSet的大小是:4
//s3和s4一样,但是stuSet将其视作两个元素
//因为Student是自定义类,Set不能判断自定义类什么时候重复,所以需要重写hashCode方法和equals方法
*/
System.out.println("-------------------0-------------------");
stuSet.add(s1);
System.out.println("-------------------1-------------------");
stuSet.add(s2);
System.out.println("-------------------2-------------------");
stuSet.add(s3);
System.out.println("-------------------3-------------------");
stuSet.add(s4);
System.out.println("-------------------4-------------------");
System.out.println("stuSet的大小是:" + stuSet.size());
/*结果显示:
-------------------0-------------------
生成hashCode
-------------------1-------------------
生成hashCode
-------------------2-------------------
生成hashCode
-------------------3-------------------
生成hashCode
equals被调用 [Jay] -- [Jay]
-------------------4-------------------
stuSet的大小是:3
* */
//当将自定义类的对象存储到set中时,会先自动调用hashCode,如果不一致,直接添加元素(不调用equals方法)
//当hashCode一致时,再调用equals进行比较,如果不一致添加元素,如果一致则舍弃
}
}
===================================================================================
package com.test.example;
import java.util.HashSet;
class Student /*extends Object*/{ //Object是所有自定义类的父类
//hashCode和equals方法是Object中已定义的方法,所有类都有这两个方法
int Sno;
String Sname = null;
public Student(int Sno, String Sname){
this.Sno = Sno;
this.Sname = Sname;
}
//hashCode先被调用,如果在集合中有与当前元素一致的hashCode,调用equals进一步比对
//1.需要重写一个方法hashCode(哈希码)
public int hashCode(){
System.out.println("生成hashCode"); //添加元素时自动调用hashCode
return Sno;
}
//2.重写equals方法
public boolean equals(Object obj){
Student s = (Student)obj;
System.out.println("equals被调用 [" + Sname + "] -- [" + s.Sname + "] ");
boolean b = Sno == s.Sno;
//boolean b = Sname.equals(s.Sname);
return b; //返回true表示一致
}
}
public class StudentSet {
public static void main(String[] args){
/**当使用HashSet存储自定义类时,需要在自定义类中重写equals和hashCode方法,
* 主要原因是集合内不允许有重复的数据元素,在集合校验元素的有效性时(数据元素不可重复),
* 需要调用equals和hashCode验证。
* */
HashSet<Student> stuSet = new HashSet<Student>();
Student s1 = new Student(1, "Lily");
Student s2 = new Student(2, "Tom");
Student s3 = new Student(3, "Jay");
Student s4 = new Student(3, "Jay");
/*stuSet.add(s1);
stuSet.add(s2);
stuSet.add(s3);
stuSet.add(s4);
//System.out.println(stuSet);
//因为没有重写toString方法,所以返回Object默认toString,显示的是元素的位置
System.out.println("stuSet的大小是:" + stuSet.size());//重写之前,stuSet的大小是:4
//s3和s4一样,但是stuSet将其视作两个元素
//因为Student是自定义类,Set不能判断自定义类什么时候重复,所以需要重写hashCode方法和equals方法
*/
System.out.println("-------------------0-------------------");
stuSet.add(s1);
System.out.println("-------------------1-------------------");
stuSet.add(s2);
System.out.println("-------------------2-------------------");
stuSet.add(s3);
System.out.println("-------------------3-------------------");
stuSet.add(s4);
System.out.println("-------------------4-------------------");
System.out.println("stuSet的大小是:" + stuSet.size());
/*结果显示:
-------------------0-------------------
生成hashCode
-------------------1-------------------
生成hashCode
-------------------2-------------------
生成hashCode
-------------------3-------------------
生成hashCode
equals被调用 [Jay] -- [Jay]
-------------------4-------------------
stuSet的大小是:3
* */
//当将自定义类的对象存储到set中时,会先自动调用hashCode,如果不一致,直接添加元素(不调用equals方法)
//当hashCode一致时,再调用equals进行比较,如果不一致添加元素,如果一致则舍弃
}
}
#7
不知道API介绍的方法底层大致什么逻辑,就看源码吧!
#1
HashSet在你调用add的时候,就会调用添加的对象的hashCode方法,而equals方法是在你某些操作的时候,比如remove,HashSet来判断到底是哪一个对象所调用的方法
#2
你去看JDK的源码就知道了!
#3
看看源码就什么都知道了。
#4
private transient HashMap<E,Object> map;
// Dummy value to associate with an Object in the backing Map
private static final Object PRESENT = new Object();
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
这是 hashset 代码中的一部分,hashset 底层是用hashmap来存储的,hashset 存储的值 存放到map中的key,value值 就是PRESENT(一个模拟值而已)。这你就该明白了吧,hashMap的 key 是不能,也不会重复的,hashmap的源码是怎么实现 key不能重复的机制,就是 set集合不能重复的实现。
#5
简单点给你说,add的时候调用equals,在你查找的时候调用hashcode,所以hashcode写得好坏会影响查找效率。
#6
下面是我自己的代码练习和理解,不知道是不是你需要的
===================================================================================
package com.test.example;
import java.util.HashSet;
class Student /*extends Object*/{ //Object是所有自定义类的父类
//hashCode和equals方法是Object中已定义的方法,所有类都有这两个方法
int Sno;
String Sname = null;
public Student(int Sno, String Sname){
this.Sno = Sno;
this.Sname = Sname;
}
//hashCode先被调用,如果在集合中有与当前元素一致的hashCode,调用equals进一步比对
//1.需要重写一个方法hashCode(哈希码)
public int hashCode(){
System.out.println("生成hashCode"); //添加元素时自动调用hashCode
return Sno;
}
//2.重写equals方法
public boolean equals(Object obj){
Student s = (Student)obj;
System.out.println("equals被调用 [" + Sname + "] -- [" + s.Sname + "] ");
boolean b = Sno == s.Sno;
//boolean b = Sname.equals(s.Sname);
return b; //返回true表示一致
}
}
public class StudentSet {
public static void main(String[] args){
/**当使用HashSet存储自定义类时,需要在自定义类中重写equals和hashCode方法,
* 主要原因是集合内不允许有重复的数据元素,在集合校验元素的有效性时(数据元素不可重复),
* 需要调用equals和hashCode验证。
* */
HashSet<Student> stuSet = new HashSet<Student>();
Student s1 = new Student(1, "Lily");
Student s2 = new Student(2, "Tom");
Student s3 = new Student(3, "Jay");
Student s4 = new Student(3, "Jay");
/*stuSet.add(s1);
stuSet.add(s2);
stuSet.add(s3);
stuSet.add(s4);
//System.out.println(stuSet);
//因为没有重写toString方法,所以返回Object默认toString,显示的是元素的位置
System.out.println("stuSet的大小是:" + stuSet.size());//重写之前,stuSet的大小是:4
//s3和s4一样,但是stuSet将其视作两个元素
//因为Student是自定义类,Set不能判断自定义类什么时候重复,所以需要重写hashCode方法和equals方法
*/
System.out.println("-------------------0-------------------");
stuSet.add(s1);
System.out.println("-------------------1-------------------");
stuSet.add(s2);
System.out.println("-------------------2-------------------");
stuSet.add(s3);
System.out.println("-------------------3-------------------");
stuSet.add(s4);
System.out.println("-------------------4-------------------");
System.out.println("stuSet的大小是:" + stuSet.size());
/*结果显示:
-------------------0-------------------
生成hashCode
-------------------1-------------------
生成hashCode
-------------------2-------------------
生成hashCode
-------------------3-------------------
生成hashCode
equals被调用 [Jay] -- [Jay]
-------------------4-------------------
stuSet的大小是:3
* */
//当将自定义类的对象存储到set中时,会先自动调用hashCode,如果不一致,直接添加元素(不调用equals方法)
//当hashCode一致时,再调用equals进行比较,如果不一致添加元素,如果一致则舍弃
}
}
===================================================================================
package com.test.example;
import java.util.HashSet;
class Student /*extends Object*/{ //Object是所有自定义类的父类
//hashCode和equals方法是Object中已定义的方法,所有类都有这两个方法
int Sno;
String Sname = null;
public Student(int Sno, String Sname){
this.Sno = Sno;
this.Sname = Sname;
}
//hashCode先被调用,如果在集合中有与当前元素一致的hashCode,调用equals进一步比对
//1.需要重写一个方法hashCode(哈希码)
public int hashCode(){
System.out.println("生成hashCode"); //添加元素时自动调用hashCode
return Sno;
}
//2.重写equals方法
public boolean equals(Object obj){
Student s = (Student)obj;
System.out.println("equals被调用 [" + Sname + "] -- [" + s.Sname + "] ");
boolean b = Sno == s.Sno;
//boolean b = Sname.equals(s.Sname);
return b; //返回true表示一致
}
}
public class StudentSet {
public static void main(String[] args){
/**当使用HashSet存储自定义类时,需要在自定义类中重写equals和hashCode方法,
* 主要原因是集合内不允许有重复的数据元素,在集合校验元素的有效性时(数据元素不可重复),
* 需要调用equals和hashCode验证。
* */
HashSet<Student> stuSet = new HashSet<Student>();
Student s1 = new Student(1, "Lily");
Student s2 = new Student(2, "Tom");
Student s3 = new Student(3, "Jay");
Student s4 = new Student(3, "Jay");
/*stuSet.add(s1);
stuSet.add(s2);
stuSet.add(s3);
stuSet.add(s4);
//System.out.println(stuSet);
//因为没有重写toString方法,所以返回Object默认toString,显示的是元素的位置
System.out.println("stuSet的大小是:" + stuSet.size());//重写之前,stuSet的大小是:4
//s3和s4一样,但是stuSet将其视作两个元素
//因为Student是自定义类,Set不能判断自定义类什么时候重复,所以需要重写hashCode方法和equals方法
*/
System.out.println("-------------------0-------------------");
stuSet.add(s1);
System.out.println("-------------------1-------------------");
stuSet.add(s2);
System.out.println("-------------------2-------------------");
stuSet.add(s3);
System.out.println("-------------------3-------------------");
stuSet.add(s4);
System.out.println("-------------------4-------------------");
System.out.println("stuSet的大小是:" + stuSet.size());
/*结果显示:
-------------------0-------------------
生成hashCode
-------------------1-------------------
生成hashCode
-------------------2-------------------
生成hashCode
-------------------3-------------------
生成hashCode
equals被调用 [Jay] -- [Jay]
-------------------4-------------------
stuSet的大小是:3
* */
//当将自定义类的对象存储到set中时,会先自动调用hashCode,如果不一致,直接添加元素(不调用equals方法)
//当hashCode一致时,再调用equals进行比较,如果不一致添加元素,如果一致则舍弃
}
}
#7
不知道API介绍的方法底层大致什么逻辑,就看源码吧!