Set最常见子类HashSet

一 HashSet概述

HashSet是Set接口的典型实现,大多时候使用Set集合时就是使用这个实现类。HashSet按Hash算法来存储集合中的元素,因此具有很好的存取和查找性能。
当向HashSet集合中存入一个元素时,HashSet会调用该对象的hashCode()方法来得到该对象的hashCode值,然后根据该HashCode值来决定该对象在HashSet中存储位置。如果有两个元素通过equals方法比较返回true,但它们的hashCode()方法返回值不相等,HashSet将会把它们存储在不同位置,也就可以添加成功。也就是说,HashSet集合判断两个元素相等的标准是通过equals()方法比较相等、并且两个对象的hashCode()方法的返回值也相等。

二 HashSet具有以下特点

  • 不能保证元素的排列顺序,顺序可能与元素的添加顺序不同,元素的顺序可能变化。
  • HashSet不是同步的,如果多个线程同时访问一个HashSet,如果有2条或者2条以上线程同时修改了HashSet集合时,必须通过代码来保证其同步。
  • 集合元素值可以是null。

三 HashSet无重复性应用

1 代码示例

import java.util.*;


// 类A的equals方法总是返回true,但没有重写其hashCode()方法
class A
{
	public boolean equals(Object obj)
	{
		return true;
	}
}
// 类B的hashCode()方法总是返回1,但没有重写其equals()方法
class B
{
	public int hashCode()
	{
		return 1;
	}
}
// 类C的hashCode()方法总是返回2,且重写其equals()方法总是返回true
class C
{
	public int hashCode()
	{
		return 2;
	}
	public boolean equals(Object obj)
	{
		return true;
	}
}
public class HashSetTest
{
	public static void main(String[] args)
	{
		HashSet books = new HashSet();
		// 分别向books集合中添加两个A对象,两个B对象,两个C对象
		books.add(new A());
		books.add(new A());
		books.add(new B());
		books.add(new B());
		books.add(new C());
		books.add(new C());
		System.out.println(books);
	}
}

2 运行结果

[B@1, B@1, C@2, A@1db9742, A@106d69c]

3 代码说明

上面代码中向books集合中分别添加了两个A对象,两个B对,两个C对象,其中C类重写 equals()方法总是返回true,hashCode()方法总是返回2,这将导致HashSet把两个C对象当成同一个对象。注意:如果需要把某个类的对象保存到HashSet集合中,重写这个类的equals()方法和hashCode()方法时,应该尽量保证两个对象通过equals()方法比较返回true时,他们的hashCode()方法返回值也相等。

 四 HashSet混乱示例

1 代码示例

import java.util.*;

class R
{
	int count;
	public R(int count)
	{
		this.count = count;
	}
	public String toString()
	{
		return "R[count:" + count + "]";
	}
	public boolean equals(Object obj)
	{
		if(this == obj)
			return true;
		if (obj != null && obj.getClass() == R.class)
		{
			R r = (R)obj;
			return this.count == r.count;
		}
		return false;
	}
	public int hashCode()
	{
		return this.count;
	}
}
public class HashSetTest2
{
	public static void main(String[] args)
	{
		HashSet hs = new HashSet();
		hs.add(new R(5));
		hs.add(new R(-3));
		hs.add(new R(9));
		hs.add(new R(-2));
		// 打印HashSet集合,集合元素没有重复
		System.out.println(hs);
		// 取出第一个元素
		Iterator it = hs.iterator();
		R first = (R)it.next();
		// 为第一个元素的count实例变量赋值
		first.count = -3;     // ①
		// 再次输出HashSet集合,集合元素有重复元素
		System.out.println(hs);
		// 删除count为-3的R对象
		hs.remove(new R(-3));    // ②
		// 可以看到被删除了一个R元素
		System.out.println(hs);
		System.out.println("hs是否包含count为-3的R对象?"
			+ hs.contains(new R(-3))); // 输出false
		System.out.println("hs是否包含count为-2的R对象?"
			+ hs.contains(new R(-2))); // 输出false
	}
}

 2 运行结果

[R[count:-2], R[count:-3], R[count:5], R[count:9]]
[R[count:-3], R[count:-3], R[count:5], R[count:9]]
[R[count:-3], R[count:5], R[count:9]]
hs是否包含count为-3的R对象?false
hs是否包含count为-2的R对象?false

3 代码说明

当程序把可变成员变量加入到HashSet中之后,尽量不要去修改该集合元素中参与计算hashCode()、equals()变量,否则将会导致HashSet无法正确操作这些集合元素。

猜你喜欢

转载自cakin24.iteye.com/blog/2323024
今日推荐