文档章节

HashSet源码分析1

Vincent-Duan
 Vincent-Duan
发布于 2016/10/24 22:25
字数 885
阅读 7
收藏 0
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

public class SetTest {
    public static void main(String[] args) {
        Set<String> set = new HashSet<>();
        set.add("abc");
        set.add("xyz");
        set.add("abc");

        for (Iterator<String> it = set.iterator(); it.hasNext();) {
            System.out.println(it.next());
        }
    }
}

查看add方法

/**
     * Adds the specified element to this set if it is not already present.
     * More formally, adds the specified element <tt>e</tt> to this set if
     * this set contains no element <tt>e2</tt> such that
     * <tt>(e==null&nbsp;?&nbsp;e2==null&nbsp;:&nbsp;e.equals(e2))</tt>.
     * If this set already contains the element, the call leaves the set
     * unchanged and returns <tt>false</tt>.
     *
     * @param e element to be added to this set
     * @return <tt>true</tt> if this set did not already contain the specified
     * element
     */
    public boolean add(E e) {
        return map.put(e, PRESENT)==null;
    }

查看map.put方法:

/**
     * Associates the specified value with the specified key in this map.
     * If the map previously contained a mapping for the key, the old
     * value is replaced.
     *
     * @param key key with which the specified value is to be associated
     * @param value value to be associated with the specified key
     * @return the previous value associated with <tt>key</tt>, or
     *         <tt>null</tt> if there was no mapping for <tt>key</tt>.
     *         (A <tt>null</tt> return can also indicate that the map
     *         previously associated <tt>null</tt> with <tt>key</tt>.)
     */
    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;
    }

关于set:当向Set集合中添加对象时,集合首先要计算出待添加对象的hashCode值,根据该值来得到一个位置,用于存放当前的对象。如果该位置没有对象存在,那么集合Set就会认为该对象在集合中不存在,直接添加进去。如果该位置已经有一个对象存在,接着将准备添加到集合中的对象与该位置上的对象进行equals方法比较,如果该equals方法返回false,那么集合认为该集合中不存在该对象的,再进行一次散列,将该对象放到散列后计算的地址里。如果equals方法返回true,那么集合认为该对象已经存在了,不会再将对象添加到集合中。

在上面的例子中我们的执行结果为:

true
true
false
abc
xyz

第三个abc插入失败了,原因就是String类型的字符串“abc”的hashcode是相等的而且equals也是相等的(equals方法使一个字符一个字符的进行比较)。

import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

public class SetTest {
    public static void main(String[] args) {
    /*    Set<String> set = new HashSet<>();
        System.out.println(set.add("abc"));
        System.out.println(set.add("xyz"));
        System.out.println(set.add("abc"));

        for (Iterator<String> it = set.iterator(); it.hasNext();) {
            System.out.println(it.next());
        }*/
        Set<People> set2 = new HashSet<>();
        set2.add(new People("zhangsan"));
        set2.add(new People("lisi"));
        set2.add(new People("zhangsan"));
        for(Iterator<People> it = set2.iterator();it.hasNext();){
            System.out.println(it.next().getName());
        }
    }
}
class People{
    String name;
    public People(String name){
        this.name = name;
    }
    public String getName(){
        return this.name;
    }
}

运行结果为:

lisi
zhangsan
zhangsan

同样是zhangsan却能添加两次。

如果想根据name来判断,修改代码如下:

import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

public class SetTest {
    public static void main(String[] args) {
        /*
         * Set<String> set = new HashSet<>();
         * System.out.println(set.add("abc"));
         * System.out.println(set.add("xyz"));
         * System.out.println(set.add("abc"));
         * 
         * for (Iterator<String> it = set.iterator(); it.hasNext();) {
         * System.out.println(it.next()); }
         */
        /*
         * String a = "abc"; String b = "abc"; System.out.println(a.hashCode());
         * System.out.println(b.hashCode());
         */
        Set<People> set2 = new HashSet<>();
        set2.add(new People("zhangsan"));
        set2.add(new People("lisi"));
        set2.add(new People("zhangsan"));
        for (Iterator<People> it = set2.iterator(); it.hasNext();) {
            System.out.println(it.next().getName());
        }
    }
}

class People {
    String name;

    public People(String name) {
        this.name = name;
    }

    public String getName() {
        return this.name;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj instanceof People) {
            People people = (People) obj;
            if(this.name.equals(people.getName()));
                return true;
        }
        return false;
    }
    @Override
    public int hashCode() {
        return this.name.hashCode();
    }
}

执行结果为:

lisi
zhangsan

当重写equals方法时,必须要重写hashCode方法。如果一个类的两个对象,使用equals方法比较时返回true,那么这两个对象必须要具有相同的hashCode。

© 著作权归作者所有

Vincent-Duan

Vincent-Duan

粉丝 10
博文 310
码字总数 119847
作品 0
海淀
私信 提问
HashSet源码分析(和HashMap非常类似)

一、看下HashSet的构造方法: private transient HashMap<E,Object> map; public HashSet() { }由HashSet的构造方法可知: 1、当我们新建一个HashSet对象的时候,HashSet会帮我们建一个HashM...

浮躁的码农
2016/02/02
24
0
Java 集合系列16之 HashSet详细介绍(源码解析)和使用示例

概要 这一章,我们对HashSet进行学习。 我们先对HashSet有个整体认识,然后再学习它的源码,最后再通过实例来学会使用HashSet。内容包括: 第1部分 HashSet介绍 第2部分 HashSet数据结构 第3...

文文1
2017/11/07
0
0
搞懂 HashSet & LinkedHashSet 源码以及集合常见面试题目

HashSet & LinkedHashSet 源码分析以及集合常见面试题目 经过上两篇的 HashMap 和 LinkedHashMap 源码分析以后,本文将继续分析 JDK 集合之 Set 源码,由于有了之前的 Map 源码分析的铺垫,S...

群星纪元
03/31
0
0
Java集合容器系列08-HashSet

一、HashSet的介绍 HashSet是一个依赖于HashMap的Set接口实现,容器的元素存储和操作都是基于内部的一个HashMap实例实现,因为这个原因,它不保证Set中元素的迭代顺序特别是不保证该顺序的恒...

老韭菜
2018/08/21
0
0
Java容器类框架分析(5)HashSet源码分析

概述 在分析HashSet源码前,先看看HashSet的继承关系 HashSet继承关系 从上图可以看出,HashSet继承自AbstractSet,实现了Set接口,接着看一下源码中的注释 This class implements the Set ...

wustor
2017/11/06
0
0

没有更多内容

加载失败,请刷新页面

加载更多

MySql双主架构原理

在企业中,一般系统架构的瓶颈会出现在数据库这一部分,mysql主从架构在很大程度上解决了这部分瓶颈,但是在mysql主从同步的架构也存在很多问题;比如:1.关于数据写入部分(也就是主库)往往很难做...

xiaomin0322
7分钟前
0
0
分布式系统中一致性哈希

问题场景 近年来B2C、O2O等商业概念的提出和移动端的发展,使得分布式系统流行了起来。分布式系统相对于单系统,解决了流量大、系统高可用和高容错等问题。功能强大也意味着实现起来需要更多...

Java领航员
9分钟前
0
0
接口限流算法:漏桶算法和令牌桶算法

漏桶算法 漏桶可以看作是一个带有常量服务时间的单服务器队列,如果漏桶(包缓存)溢出,那么数据包会被丢弃。这一点和线程池原理是很相似的。 把请求比作是水,水来了都先放进桶里,并以限定...

铁骨铮铮
16分钟前
0
0
Android 生成二维码工具类

/** * 生成条形码和二维码的工具 */public class ZXingUtils { /** * 生成二维码 要转换的地址或字符串,可以是中文 * * @param url * @param width ...

lanyu96
21分钟前
0
0
谈谈lucene的DocValues特性之SortedNumericDocValuesField

前面已经介绍过NumericDocValuesField,这里想强调一下SortedNumericDocValuesField是针对同一篇文档中一键多值的情况进行排序的,换句话说不同文档的同一字段值可以乱序。核心的写入流程与谈...

FAT_mt
29分钟前
0
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部