HashSet集合的add()方法的源码

interface Collection {
    ...
}

interface Set extends Collection {
    ...
}

class HashSet implements Set {
    private static final Object PRESENT = new Object();
    private transient HashMap<E,Object> map;

    public HashSet() {
        map = new HashMap<>();
    }

    public boolean add(E e) { //e=hello,world
        return map.put(e, PRESENT)==null;
    }
}

class HashMap implements Map {
    public V put(K key, V value) { //key=e=hello,world

        //看哈希表是否为空,如果空,就开辟空间
        if (table == EMPTY_TABLE) {
            inflateTable(threshold);
        }

        //判断对象是否为null
        if (key == null)
            return putForNullKey(value);

        int hash = hash(key); //和对象的hashCode()方法相关

        //在哈希表中查找hash值
        int i = indexFor(hash, table.length);
        for (Entry<K,V> e = table[i]; e != null; e = e.next) {
            //这次的e其实是第一次的world
            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;
    }

    transient int hashSeed = 0;

    final int hash(Object k) { //k=key=e=hello,
        int h = hashSeed;
        if (0 != h && k instanceof String) {
            return sun.misc.Hashing.stringHash32((String) k);
        }

        h ^= k.hashCode(); //这里调用的是对象的hashCode()方法

        // This function ensures that hashCodes that differ only by
        // constant multiples at each bit position have a bounded
        // number of collisions (approximately 8 at default load factor).
        h ^= (h >>> 20) ^ (h >>> 12);
        return h ^ (h >>> 7) ^ (h >>> 4);
    }
}

//对应源码 addEntry(hash, key, value, i); //把元素添加

void addEntry(int hash, K key, V value, int bucketIndex) {
    Entry<K,V> e = table[bucketIndex];
        table[bucketIndex] = new Entry<K,V>(hash, key, value, e);
        if (size++ >= threshold)
            resize(2 * table.length);
    }

    hs.add("hello");//由于table里面没有,直接进addEntry

    hs.add("world");//同上

    hs.add("java");//同上

    hs.add("world");//table里已经存在,(hash和equals相等),则在put()时,用新值替代老值。ps:这里的老值和新值都是world。

总结:


首先比较哈希值

    如果相同,继续走,比较地址值或者走equals()

    如果不同,就直接添加到集合中

按照方法的步骤来说:

    先看hashCode()值是否相同

        相同:继续走equals()方法

            返回true:说明元素重复,就不添加

            返回false:说明元素不重复,就添加到集合

        不同:就直接把元素添加到集合

如果类没有重写这两个方法,默认使用的Object()。一般磊说不会相同。

而String类重写了hashCode()和equals()方法,所以,它就把内容相同的字符串去掉,只留下一个。

时间: 2024-10-11 17:39:26

HashSet集合的add()方法的源码的相关文章

java 17 - 6 TreeSet集合及其add()方法的源码解析

TreeSet:能够对元素按照某种规则进行排序. 排序有两种方式 A:自然排序 B:比较器排序 TreeSet集合的特点:排序和唯一 1 public class TreeSetDemo { 2 public static void main(String[] args) { 3 // 创建集合对象 4 // 自然顺序进行排序 5 TreeSet<Integer> ts = new TreeSet<Integer>(); 6 7 // 创建元素并添加 8 // 20,18,23,2

深入理解JAVA集合系列四:ArrayList源码解读

在开始本章内容之前,这里先简单介绍下List的相关内容. List的简单介绍 有序的collection,用户可以对列表中每个元素的插入位置进行精确的控制.用户可以根据元素的整数索引(在列表中的位置)访问元素,并搜索列表中的元素.列表通常允许重复的元素,且允许null元素的存放. ArrayList的简单介绍 JDK中这样定义ArrayList:List接口的大小可变数据的实现. 主要有以下特点: 1.有序 2.线程不安全 3.元素可以重复 4.可以存放null值 顾名思义,取名ArrayLis

RxJava1.0 flatMap方法的源码分析

RxJava1.0 flatMap方法的源码分析 package com.yue.test; import java.awt.Cursor; import java.util.ArrayList; import java.util.List; import com.yue.bean.Course; import com.yue.bean.Student; import rx.Observable; import rx.Subscription; import rx.Observable.OnSu

C# 验证码识别基础方法及源码

原文:C# 验证码识别基础方法及源码 先说说写这个的背景 最近有朋友在搞一个东西,已经做的挺不错了,最后想再完美一点,于是乎就提议把这种验证码给K.O.了,于是乎就K.O.了这个验证码.达到单个图片识别时间小于200ms,500个样本人工统计正确率为95%.由于本人没有相关经验,是摸着石头过河.本着经验分享的精神,分享一下整个分析的思路.在各位大神面前献丑了. 再看看部分识别结果 是不是看着很眼熟? 处理第一步 去背景噪音和二值化 对于这一块,考虑了几种方法. 方法一:统计图片颜色分布,颜色占有

json.net 比jsonIgnore 更好的方法 修改源码

关于 JsonIgnore  问题, EF T4 模板 中 存在主外键关系 namespace WindowsFormsApplication1{    using System;    using System.Collections.Generic;    using Newtonsoft.Json;    public partial class MaterielProcedures    {        public int Kid { get; set; }        publi

Linux/Unix分配进程ID的方法以及源码实现

在Linux/Unix系统中,每个进程都有一个非负整型表示的唯一进程ID.虽然是唯一的,但是进程的ID可以重用.当一个进程终止后,其进程ID就可以再次使用了.大多数Linux/Unix系统采用延迟重用的算法,使得赋予新建进程ID不同于最近终止进程所使用的ID,这主要是为了防止将新进程误认为是使用同一ID的某个已终止的先前进程.本文讨论了Linux/Unix分配进程ID的方法以及源码实现. 分配进程ID的方法 在大多数Linux/Unix系统中,生成一个进程ID方法是:从0开始依次连续分配,一直到

Scala 深入浅出实战经典 第41讲:List继承体系实现内幕和方法操作源码揭秘

package com.parllay.scala.dataset /** * Created by richard on 15-7-25. * 第41讲:List继承体系实现内幕和方法操作源码揭秘 */object List_Interal { def main(args: Array[String]) { /** * List: 继承体系: * list有两个子类 Nil, ::, 他们都实现了 * override def head : B = hd override def tail :

.Net 转战 Android 4.4 日常笔记(9)--常用组件的使用方法[附源码]

原文:.Net 转战 Android 4.4 日常笔记(9)--常用组件的使用方法[附源码] 经过两天的学习,把常用的组件都学习了一遍,并做成了App 学习可能真没有捷径,跟学习html有点类似,都是一个控件一个控件学习并使用,最后拼凑成一个系统 链接:http://pan.baidu.com/s/1hqefzEW 密码:zbel  最低API 2.3 目标API 4.4 采用Android Studio 0.58IDE 希望给和我同样的初学者带来一些便利,和开发时候可以查询,第一个版本可能比较

hadoop学习;hdfs操作;运行抛出权限异常: Permission denied;api查看源码方法;源码不停的向里循环;抽象类通过debug查找源码

eclipse快捷键alt+shift+m将选中的代码封装成方法:alt+shift+l将选中的代码添加对应类型放回参数 当调用一个陌生方法时,进入源码不停的向里循环,当找不到return类似方法的时候,可以看到最原始的方法 package com.kane.hdfs; import java.io.InputStream; import java.net.URL; import org.apache.hadoop.fs.FsUrlStreamHandlerFactory; import org