HashSet 实现类

时间:2024-03-16 08:46:03

HashSet 实现类

通过 HashCode 判断元素是否存在,若存在则不添加,否则添加以此实现唯一性

常用方法

Modifier and Type Method and Description
boolean add(E e) 将指定的元素添加到此集合(如果尚未存在)。
void clear() 从此集合中删除所有元素。
Object clone() 返回此 HashSet实例的浅层副本:元素本身不被克隆。
boolean contains(Object o) 如果此集合包含指定的元素,则返回 true
boolean isEmpty() 如果此集合不包含元素,则返回 true
Iterator<E> iterator() 返回此集合中元素的迭代器。
boolean remove(Object o) 如果存在,则从该集合中删除指定的元素。
int size() 返回此集合中的元素数(其基数)。
Spliterator<E> spliterator() 在此集合中的元素上创建late-binding故障快速 Spliterator

演示

import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
public class SetDemo {
	public static void main(String[] args) {
		HashSet<String> test = new HashSet<>();
		test.add("华为");
		test.add("苹果");
		test.add("小米");
		System.out.println(test.size());
		System.out.println(test);
		//添加重复元素不会进行添加
		test.add("华为");
		System.out.println(test);
		//删除元素,由于 Set 集合没有下标,只能指定值进行删除
		test.remove("华为");
		System.out.println(test);
		//再次添加进来是无序的
		test.add("华为");
		//集合的遍历
		System.out.println("-----for------");
		for (String string : test) {
			System.out.println(string);
		}
		System.out.println("-----Iterator-----");
		Iterator<String> it = test.iterator();
		while(it.hasNext()) {
			System.out.println(it.next());
		}
	}
}

重写 hashcode equals 方法

注:可以通过系统根据自动重写这两个方法

存储方式

  1. 根据元素的 hashcode 方法计算存储位置,若该位置不存在元素则直接储存,否则进入下一步
  2. 再执行 equals 方法,若返回值为 true 则认为元素重复;否则形成链表

image-20220120155908304

未重写方法时

import java.util.HashSet;
public class HashDemo {
	private String name;
	private int age;
	public HashDemo(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 "HashDemo [name=" + name + ", age=" + age + "]";
	}
	public static void main(String[] args) {
		HashSet<HashDemo> hash = new HashSet<>();
		HashDemo h1 = new HashDemo("张三", 20);
		HashDemo h2 = new HashDemo("李四", 21);
		HashDemo h3 = new HashDemo("王五", 22);
		hash.add(h1);
		hash.add(h2);
		hash.add(h3);
		System.out.println(hash.size());
		System.out.println(hash);
		//添加同一个对象将会被equals判别为true,将不会将此对象进行再次添加
		hash.add(h1);
		System.out.println(hash.size());
		System.out.println(hash);
		//但是对于相同的值的不同对象,却能够添加
		hash.add(new HashDemo("张三", 20));
		System.out.println(hash.size());
         System.out.println(hash);
	}
}

未对方法进行重写之前,对于用于相同数值的不同对象依然能够进行添加,这样不符合不重复的法则,因此对方法进行重写,重写后代码如下

只对 hanshcode 进行重写后的代码如下

import java.util.HashSet;
public class HashDemo {
	private String name;
	private int age;
	public HashDemo(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 "HashDemo [name=" + name + ", age=" + age + "]";
	}
	
	//重写hashcode方法使hashcode只与姓名和年龄有关
	@Override
	public int hashCode() {
		int n1 = this.name.hashCode();
		int n2 = this.age;
		return n1 + n2;
	}
	public static void main(String[] args) {
		HashSet<HashDemo> hash = new HashSet<>();
		HashDemo h1 = new HashDemo("张三", 20);
		HashDemo h2 = new HashDemo("李四", 21);
		HashDemo h3 = new HashDemo("王五", 22);
		hash.add(h1);
		hash.add(h2);
		hash.add(h3);
		System.out.println(hash.size());
		System.out.println(hash);
		//添加同一个对象将会被equals判别为true,将不会将此对象进行再次添加
		hash.add(h1);
		System.out.println(hash.size());
		System.out.println(hash);
		//但是对于相同的值的不同对象,却能够添加
		hash.add(new HashDemo("张三", 20));
		System.out.println(hash.size());
		System.out.println(hash);
	}
}
3
[HashDemo [name=王五, age=22], HashDemo [name=张三, age=20], HashDemo [name=李四, age=21]]
3
[HashDemo [name=王五, age=22], HashDemo [name=张三, age=20], HashDemo [name=李四, age=21]]
4
[HashDemo [name=王五, age=22], HashDemo [name=张三, age=20], HashDemo [name=张三, age=20], HashDemo [name=李四, age=21]]

依然能够添加值相同的元素,但是与未重写 hashcode 时的区别在于,新添加的元素没有放在数组中,由于其与 p1 拥有相同的 hashcode 因此新的元素和 p1 占用相同的位置,只是形成链表存储

如图

image-20220120202719283

为不添加值相同的元素,因此再以上基础上还需对 equals 进行重写

import java.util.HashSet;
public class HashDemo {
	private String name;
	private int age;
	public HashDemo(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 "HashDemo [name=" + name + ", age=" + age + "]";
	}
	
	//重写hashcode方法使hashcode只与姓名和年龄有关
	@Override
	public int hashCode() {
		int n1 = this.name.hashCode();
		int n2 = this.age;
		return n1 + n2;
	}
	//重写equals,当值相等时返回true
	@Override
	public boolean equals(Object obj) {
		if(obj == null) {
			return false;
		}
		if(this == obj) {
			return true;
		}
		if(obj instanceof HashDemo) {
			HashDemo h = (HashDemo)obj;
			if(this.name.equals(h.name) && this.age == h.age) {
				return true;
			} else {
				return false;
			}
		}
		return false;
	}
	
	public static void main(String[] args) {
		HashSet<HashDemo> hash = new HashSet<>();
		HashDemo h1 = new HashDemo("张三", 20);
		HashDemo h2 = new HashDemo("李四", 21);
		HashDemo h3 = new HashDemo("王五", 22);
		hash.add(h1);
		hash.add(h2);
		hash.add(h3);
		System.out.println(hash.size());
		System.out.println(hash);
		//添加同一个对象将会被equals判别为true,将不会将此对象进行再次添加
		hash.add(h1);
		System.out.println(hash.size());
		System.out.println(hash);
		//但是对于相同的值的不同对象,却能够添加
		hash.add(new HashDemo("张三", 20));
		System.out.println(hash.size());
		System.out.println(hash);
        //hash.remove(new HashDemo("张三", 20));	重写equals后可使用这种方式删除	
	}
}
3
[HashDemo [name=王五, age=22], HashDemo [name=张三, age=20], HashDemo [name=李四, age=21]]
3
[HashDemo [name=王五, age=22], HashDemo [name=张三, age=20], HashDemo [name=李四, age=21]]
3
[HashDemo [name=王五, age=22], HashDemo [name=张三, age=20], HashDemo [name=李四, age=21]]

可见,此时将不会添加相同的元素(对于人而言相同的)。