集合中的HashSet底层是通过Hash表实现,HashSet的特点是元素唯一,但用到Hash表就跟hashCode()有了密不可分的联系,所以HashSet的唯一性是通过hashCode()方法来保证,当然光有HashCode()还不够,还有equals()也用到。从底层(HashMap的put()方法)实现代码来看,就可以清楚地看到这一点。

    public V put(K key, V value) {
if (table == EMPTY_TABLE) {
inflateTable(threshold);
}
if (key == null)
return putForNullKey(value);
int hash = hash(key);
int i = indexFor(hash, table.length);
for (Entry<K,V> e = table[i]; e != null; e = e.next) {
Object k;
if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
V oldValue = e.value;
e.value = value;
e.recordAccess(this);
return oldValue;
}
} modCount++;
addEntry(hash, key, value, i);
return null;
}

以上代码是HashMap的put()方法源码。那么HashSet到底是如何保证元素的唯一性,还是通过例子来说明这一点。同样,还是以HashSet存储自定义对象为例,先创建一个Person类,成员:name、age、无参、有参构造及getters和setters。

package cn.dolphin;

public class Person{
private String name;
private int 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;
}
public Person(String name, int age) {
super();
this.name = name;
this.age = age;
}
public Person() {
super();
// TODO Auto-generated constructor stub
}
@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + "]";
}
}

这里先没有重写继承自Object类的hashCode()和equals()方法。

package cn.dolphin;

import java.util.HashSet;
import java.util.Iterator;
import java.util.Set; public class HashSetDemo {
public static void main(String[] args) {
//创建集合对象
Set<Person> set = new HashSet<>();
//创建对象元素
Person p1 = new Person("诸葛亮",39);
Person p2 = new Person("赵子龙",36);
Person p3 = new Person("关云长",38);
Person p4 = new Person("关云长",38);
Person p5 = new Person("关云长",38);
//将对象元素添加到集合
set.add(p1);
set.add(p2);
set.add(p3);
set.add(p4);
set.add(p5);
//使用iterator()对集合遍历
for (Iterator<Person> it = set.iterator(); it.hasNext();) {
Person p = it.next();
System.out.println(p);
}
}
}

运行程序的结果看到[关云长,38]出现了三次,这说明没有保证元素的唯一。现在我们在Person类中重写hashCode()和equals()。使用eclipse直接右键"Source->Generate hashCode() and equals()..."自动生成代码。

    @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 (!(obj instanceof Person))
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;
}

再次运行程序,看到结果中[关云长,38]只出现了一次,说明已经对相同元素进行了过滤。再回过头来看看上面的源代码,里面的if语句"if (e.hash == hash && ((k = e.key) == key || key.equals(k)))"使用的"&&",这个逻辑运算符的使用,还是有必要在这里啰嗦一下,只有"&&"的左边为true才会对右边进行判断,左边如果false,就不再看右边,这不禁使我想起一道面试题,扯远了,这个最后说。那么"&&"用在这里就意味着,如果左边的hashCode()判断false,会直接添加元素,不用再判断equals(),如果左边true,才会继续判断equals()。因为"&&"具有短路功能,这就是HashSet保证元素唯一的原理。
  下面说说刚才提到的面试题。

        int x = 1, y = 1;
if(x++ > 3 && ++y > 3){
++x;
y++;
}
System.out.println(x);
System.out.println(y);

集合HashSet的使用的更多相关文章

  1. Java集合 -- HashSet 和 HashMap

    HashSet 集合 HashMap 集合 HashSet集合 1.1 Set 接口的特点 Set体系的集合: A:存入集合的顺序和取出集合的顺序不一致 B:没有索引 C:存入集合的元素没有重复 1. ...

  2. java集合-HashSet

    HashSet 概述 对于 HashSet 而言,它是基于 HashMap 实现的,底层采用 HashMap 来保存元素,所以如果对 HashMap 比较熟悉了,那么学习 HashSet 也是很轻松的 ...

  3. Java 集合 - HashSet

    一.源码解析 public class HashSet<E> extends AbstractSet<E> implements Set<E>, Cloneable ...

  4. Java集合---HashSet的源码分析

    一.  HashSet概述: HashSet实现Set接口,由哈希表(实际上是一个HashMap实例)支持.它不保证set 的迭代顺序:特别是它不保证该顺序恒久不变.此类允许使用null元素. 二.  ...

  5. Set集合——HashSet、TreeSet、LinkedHashSet(2015年07月06日)

    一.Set集合不同于List的是: Set不允许重复 Set是无序集合 Set没有下标索引,所以对Set的遍历要通过迭代器Iterator 二.HashSet 1.HashSet由一个哈希表支持,内部 ...

  6. 哈希集合——hashSet

    /**     哈希集合特点:存取顺序不确定,同一个哈希值的位置可以存放多个元素,                   哈希集合存放元素的时候是先判断哈希地址值:hashCode()是否相同,如果不同 ...

  7. ava集合---HashSet的源码分析

    一.HasnSet概述 Hashset实现set接口,由哈希表(实际上是一个HashMap实例)支持.它不保证set的迭代顺序.特别是它不保证该顺序恒久不变.此类允许使用Null元素 一.HasnSe ...

  8. java集合-HashSet源码解析

    HashSet 无序集合类 实现了Set接口 内部通过HashMap实现 // HashSet public class HashSet<E> extends AbstractSet< ...

  9. 集合-HashSet

    参考博客:https://www.cnblogs.com/runwulingsheng/p/5208762.html https://www.cnblogs.com/ysocean/p/6555373 ...

  10. 基于散列的集合 HashSet\HashMap\HashTable

    HashSet\HashMap\HashTable 1 基于散列的集合 2 元素会根据hashcode散列,因此,集合中元素的顺序不一定与插入的顺序一致. 3 根据equals方法与hashCode方 ...

随机推荐

  1. 基于redis ae实现 Linux中的文件系统监控机制(inotify)

    (英文部分为转的.代码是个人代码) 1 What's inotify  The inotify API provides a mechanism for monitoring file system ...

  2. hdu Escape

    Escape 题目: 非常裸的多重匹配. 可是点数较多,所以要用到状态压缩. . .. .. 第一次写. 好厉害的赶脚. #include <iostream> #include < ...

  3. 如何用Android studio生成正式签名的APK文件

    必须签名之后才可以发布到app商店中. 平时的调试的app都有默认的签名. 下面是生成带签名的APK的步骤: 1. Build 选择 Generate Signed APK 2. 弹出框,第一次选择C ...

  4. nyoj--814--又见拦截导弹(动态规划+贪心)

    又见拦截导弹 时间限制:3000 ms  |  内存限制:65535 KB 难度:3 描述 大家对拦截导弹那个题目应该比较熟悉了,我再叙述一下题意:某国为了防御敌国的导弹袭击,新研制出来一种导弹拦截系 ...

  5. vue 如何实现在函数中触发路由跳转

    this.$router.push({path:'/index'}) 欢迎加入前端交流群交流知识&&获取视频资料:749539640 methods:{ click(){ if(dat ...

  6. 90.bower解决js的依赖管理

    转自:https://blog.csdn.net/u011537073/article/details/52951122 前言一个新的web项目开始,我们总是很自然地去下载需要用到的js类库文件,比如 ...

  7. 解码URLDecode和编码URLEnCode

    在前台往后台传递参数的时候,在前台进行编码,在后台接收参数的时候,用Decode进行解码: 如果url中包含特殊字符如:&.html标签 <tr><td>等导致url无 ...

  8. 【原创】websphere部署war包报错

    应用程序在Tomcat上运行一切正常,但在websphere上部署时报以下错误:错误 500 处理请求时发生一个错误: /admin/upload.do 消息: WEB-INF/web.xml 详细错 ...

  9. 如何设置ASP.NET站点页面运行超时

    全局超时时间 服务器上如果有多个网站,希望统一设置一下超时时间,则需要设置 Machine.config 文件中的 ExecutionTimeout 属性值.Machine.config 文件位于 % ...

  10. RocketMQ学习笔记(2)----Broker的集群四种方式

    RocketMQ推荐了几种Broker集群方式,这里的Slave不可写,但可读,类似于Mysql主备方式 1. 单个Master 这是一种风险比较大的集群方式,因为一旦Borker重启或宕机期间,将会 ...