HashSet插入流程分析

我们都用过java里面的HashSet,也都知道这是通过哈希表(数组+链表)实现的。但是我们在使用过程中,很难感觉到散列表的特征,可以通过实验分析。

元素类型

public class Person {
	
	private String name;
	private int age;
	public Person()
	{
		
	}
	public Person(String name, int age) {
		super();
		this.name = name;
		this.age = age;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public int getAge() {
		return age;
	}
	public void setAge(int age) {
		this.age = age;
	}
	@Override
	public String toString() {
		return "Person [name=" + name + ", age=" + age + "]";
	}
}

插入相同的元素

public class HashSetTest2 {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		HashSet<Person> hs=new HashSet();
		Person p1=new Person("张三",20);
		Person p2=new Person("李四",22);
		Person p3=new Person("王五",24);
		hs.add(p1);
		hs.add(p2);
		hs.add(p3);
		hs.add(p1);
		System.out.println(hs.toString());
	}
}
[Person [name=李四, age=22], Person [name=王五, age=24], Person [name=张三, age=20]]

众所周知,显然不行

插入值相同的不同元素,引用地址不一样,成员变量值一样

public class HashSetTest2 {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		HashSet<Person> hs=new HashSet();
		Person p1=new Person("张三",20);
		Person p2=new Person("李四",22);
		Person p3=new Person("王五",24);
		Person p4=new Person("张三",20);
		hs.add(p1);
		hs.add(p2);
		hs.add(p3);
		hs.add(p4);
        System.out.println(p1.hashCode());
        System.out.println(p4.hashCode());
		System.out.println(hs.toString());

	}

}

366712642
1311053135
[Person [name=王五, age=24], Person [name=张三, age=20], Person [name=李四, age=22], Person [name=张三, age=20]]

可以,哈希值不一样

如果我们重写hashcode函数(默认哈希值和地址有关)

public class Person {
	
	private String name;
	private int age;
	public Person()
	{
		
	}
	public Person(String name, int age) {
		super();
		this.name = name;
		this.age = age;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public int getAge() {
		return age;
	}
	public void setAge(int age) {
		this.age = age;
	}
	@Override
	public String toString() {
		return "Person [name=" + name + ", age=" + age + "]";
	}
	
	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + age;
		result = prime * result + ((name == null) ? 0 : name.hashCode());
		return result;
	}
	
	

}

public class HashSetTest2 {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		HashSet<Person> hs=new HashSet();
		Person p1=new Person("张三",20);
		Person p2=new Person("李四",22);
		Person p3=new Person("王五",24);
		Person p4=new Person("张三",20);
		hs.add(p1);
		hs.add(p2);
		hs.add(p3);
		hs.add(p4);
        System.out.println(p1.hashCode());
        System.out.println(p4.hashCode());
		System.out.println(hs.toString());

	}

}

776470
776470
[Person [name=李四, age=22], Person [name=王五, age=24], Person [name=张三, age=20], Person [name=张三, age=20]]

HashSet认为,这两个哈希值相同,但是不是一个元素,可以加入。因为默认的equals函数只与地址有关。

现在不重写hashcode,只重写equals

public class Person {
	
	private String name;
	private int age;
	public Person()
	{
		
	}
	public Person(String name, int age) {
		super();
		this.name = name;
		this.age = age;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public int getAge() {
		return age;
	}
	public void setAge(int age) {
		this.age = age;
	}
	@Override
	public String toString() {
		return "Person [name=" + name + ", age=" + age + "]";
	}
	
	
	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Person other = (Person) obj;
		if (age != other.age)
			return false;
		if (name == null) {
			if (other.name != null)
				return false;
		} else if (!name.equals(other.name))
			return false;
		return true;
	}
	
	

}

366712642
1311053135
[Person [name=王五, age=24], Person [name=张三, age=20], Person [name=李四, age=22], Person [name=张三, age=20]]

哈希值不同,自然可以插入。

两个方法都重写

package myset;

public class Person {
	
	private String name;
	private int age;
	public Person()
	{
		
	}
	public Person(String name, int age) {
		super();
		this.name = name;
		this.age = age;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public int getAge() {
		return age;
	}
	public void setAge(int age) {
		this.age = age;
	}
	@Override
	public String toString() {
		return "Person [name=" + name + ", age=" + age + "]";
	}
	
	
	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + age;
		result = prime * result + ((name == null) ? 0 : name.hashCode());
		return result;
	}
	
	
	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Person other = (Person) obj;
		if (age != other.age)
			return false;
		if (name == null) {
			if (other.name != null)
				return false;
		} else if (!name.equals(other.name))
			return false;
		return true;
	}
	
	

}

776470
776470
[Person [name=李四, age=22], Person [name=王五, age=24], Person [name=张三, age=20]]

终于插不进去了。
可见。HashSet插入过程是先比较哈希值,与原有元素都不同的话,直接插入。
相同的话,使用equals方法,相等就不插入。

另外需要说明,remove方法也是先哈希后equals

你可能感兴趣的:(HashSet插入流程分析)