HashSet如何保证元素不重复(面试必问)
本文已收录《java常见面试题》系列,git 开源地址:https://gitee.com/mydb/interview
hashset 实现了 set 接口,由哈希表(实际是 hashmap)提供支持。hashset 不保证集合的迭代顺序,但允许插入 null 值。也就是说 hashset 不能保证元素插入顺序和迭代顺序相同。
hashset 具备去重的特性,也就是说它可以将集合中的重复元素自动过滤掉,保证存储在 hashset 中的元素都是唯一的。
1.hashset 基本用法
hashset 基本操作方法有:add(添加)、remove(删除)、contains(判断某个元素是否存在)和 size(集合数量)。这些方法的性能都是固定操作时间,如果哈希函数是将元素分散在桶中的正确位置。
hashset 基本使用如下:
// 创建 hashset 集合 hashset<string> strset = new hashset<>(); // 给 hashset 添加数据 strset.add("java"); strset.add("mysql"); strset.add("redis"); // 循环打印 hashset 中的所有元素 strset.foreach(s -> system.out.println(s));
2.hashset 无序性
hashset 不能保证插入元素的顺序和循环输出元素的顺序一定相同,也就是说 hashset 其实是无序的集合,具体代码示例如下:
hashset<string> mapset = new hashset<>(); mapset.add("深圳"); mapset.add("北京"); mapset.add("西安"); // 循环打印 hashset 中的所有元素 mapset.foreach(m -> system.out.println(m));
以上程序的执行结果如下:
从上述代码和执行结果可以看出,hashset 插入的顺序是:深圳 -> 北京 -> 西安,而循环打印的顺序却是:西安 -> 深圳 -> 北京,所以 hashset 是无序的,不能保证插入和迭代的顺序一致。
ps:如果要保证插入顺序和迭代顺序一致,可使用 linkedhashset 来替换 hashset。
3.hashset 错误用法
有人说 hashset 只能保证基础数据类型不重复,却不能保证自定义对象不重复?这样说对吗?
我们通过以下示例来说明此问题。
3.1 hashset 与基本数据类型
使用 hashset 存储基本数据类型,实现代码如下:
hashset<long> longset = new hashset<>(); longset.add(666l); longset.add(777l); longset.add(999l); longset.add(666l); // 循环打印 hashset 中的所有元素 longset.foreach(l -> system.out.println(l));
以上程序的执行结果如下:
从上述结果可以看出,使用 hashset 可以保证基础数据类型不重复。
3.2 hashset 与自定义对象类型
接下来,将自定义对象存储到 hashset 中,实现代码如下:
public class hashsetexample { public static void main(string[] args) { hashset<person> personset = new hashset<>(); personset.add(new person("曹操", "123")); personset.add(new person("孙权", "123")); personset.add(new person("曹操", "123")); // 循环打印 hashset 中的所有元素 personset.foreach(p -> system.out.println(p)); } } @getter @setter @tostring class person { private string name; private string password; public person(string name, string password) { this.name = name; this.password = password; } }
以上程序的执行结果如下:
从上述结果可以看出,自定义对象类型确实没有被去重,那也就是说 hashset 不能实现自定义对象类型的去重咯?
其实并不是,hashset 去重功能是依赖元素的 hashcode 和 equals 方法判断的,通过这两个方法返回的都是 true 那就是相同对象,否则就是不同对象。而前面的 long 类型元素之所以能实现去重,正是因为 long 类型中已经重写了 hashcode 和 equals 方法,具体实现源码如下:
@override public int hashcode() { return long.hashcode(value); } public boolean equals(object obj) { if (obj instanceof long) { return value == ((long)obj).longvalue(); } return false; } //省略其他源码......