HashSet、HashMap

本文深入解析了HashSet如何基于HashMap实现元素的添加与查找操作。详细分析了HashMap内部的工作机制,包括hash值的计算、节点的存储及扩容过程,并通过具体示例展示了不同情况下的元素比较逻辑。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

HashSet添加元素方法的本质也是调用HashMap
这里写图片描述

HashMap实现原理:
移位、^运算=hash值
能否添加进HashMap集合当中呢?
3个依据:判断hash值、是否为同一对象、equals方法是否相等
e.hash == hash && ((k = e.key) == key || key.equals(k))

Entry<K,V> e = table[i]; e != null; e = e.next
//取桶里的对象,如果桶里没有对象即立刻添加进来,如果有需要,可以进行桶的扩容
void addEntry(int hash, K key, V value, int bucketIndex) {
        if ((size >= threshold) && (null != table[bucketIndex])) {
            resize(2 * table.length);
            hash = (null != key) ? hash(key) : 0;
            bucketIndex = indexFor(hash, table.length);
        }

        createEntry(hash, key, value, bucketIndex);
    }


  void createEntry(int hash, K key, V value, int bucketIndex) {
        Entry<K,V> e = table[bucketIndex];
        table[bucketIndex] = new Entry<>(hash, key, value, e);
        size++;
    }
Entry<K,V> e = table[i]取桶里的对象,如果桶里没有对象即立刻添加进来,如果有对象则判断桶里的hash值是否等于要放进桶里的对象的hash值,如果相等,再判断是否为同一个对象。

Entry<K,V> e = table[i]取桶里的对象,判断桶里的hash值和将要放进桶里的对象的hash值是否相等,如相等,再判断是否为同一个对象,根据(k = e.key) == keykey值来做判断,如果为同一个对象,说明桶里已经存在该key则为同一个对象, V oldValue = e.value即用药放进桶里的value值取代原来桶里的value值。

Entry<K,V> e = table[i]即用来取桶里的元素,有可能存在,也有可能不存在,如果hash值一样即e.hash == hash为true,但是(k = e.key) == keyfalse,说明不是同一个对象。再判断key.equals(k)即判断该对象的equals方法是否相等。如果相等, V oldValue = e.value即用药放进桶里的value值取代原来桶里的value值。
    public V put(K key, V value) {
        if (key == null)
            return putForNullKey(value);
        int hash = hash(key);  //计算新hash
        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;
    }

int hash(key)=原hash移位得到新hash并和原hash进行^异或运算

  final int hash(Object k) {
        int h = 0;
        if (useAltHashing) {
            if (k instanceof String) {
                return sun.misc.Hashing.stringHash32((String) k);
            }
            h = hashSeed;
        }

        h ^= k.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);
    }

e = e.next创建新节点

 static class Entry<K,V> implements Map.Entry<K,V> {
        final K key;
        V value;
        Entry<K,V> next;
        int hash;

        /**
         * Creates new entry.
         */
        Entry(int h, K k, V v, Entry<K,V> n) {
            value = v;
            next = n;
            key = k;
            hash = h;
        }

===================================

判断HashSet集合是否已经存在某个对象
代码:

package com.zbiti.java;

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

import org.junit.Test;

public class SetDemo {
    public static void main(String[] args) {

    }


    @Test
    public void test1(){
        Set<Dog> dogs = new HashSet<Dog>();
        dogs.add(new Dog("white"));
        dogs.add(new Dog("white"));


        System.out.println(dogs.contains(new Dog("white")));    //fasle
    }
}


class Dog{
    private String name;
    public Dog(String name) {
        this.name = name;
    }

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

结果:输出false
原因:HashSet的contains方法的实现如下:

 public boolean contains(Object o) {
        return map.containsKey(o);
    }
 public boolean containsKey(Object key) {
        return getEntry(key) != null;
    }
 final Entry<K,V> getEntry(Object key) {
        int hash = (key == null) ? 0 : hash(key);
        for (Entry<K,V> e = table[indexFor(hash, table.length)];
             e != null;
             e = e.next) {
            Object k;
            if (e.hash == hash &&
                ((k = e.key) == key || (key != null && key.equals(k))))
                return e;
        }
        return null;
    }

解读:

int hash = (key == null) ? 0 : hash(key);
先判断添加方法里是否有对象,如果有对象计算出新hash值,
 for (Entry<K,V> e = table[indexFor(hash, table.length)]; e != null;e = e.next)
 先判断桶里是否有对象,如果有对象取出对象
 if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k))))
   return e;
    return null;
   判断hash值是否相等,即放进桶里对象和已经存在桶里对象的hash值是否相等。如不想等,返回null,说明桶里不存在该对象。
   如相等,再判断对象是否为同一个对象。
   如果是同一对象,返回该对象。
   如果不是同一个对象,再判断对象的equals是否相等,即桶里的对象和将要放进桶里的对象的equals方法的比较。

代码:

package com.zbiti.java;

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

import org.junit.Test;

public class SetDemo {
    public static void main(String[] args) {

    }


    @Test
    public void test1(){
        Set<Dog> dogs = new HashSet<Dog>();
        dogs.add(new Dog("white"));
        dogs.add(new Dog("white"));


        System.out.println(dogs.contains(new Dog("white")));  //false
    }
}


class Dog{
    private String name;
    public Dog(String name) {
        this.name = name;
    }

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

    @Override
    public int hashCode() {
        return 1;
    }
}

结果:输出false
分析:Dog类的hashCode方法被我们重写了,根据上面的分析,桶里2只dog和contains里的dog的hash值是一样的都为1,。所以需要再判断是否 为同一个对象,明显通过new 产生了新的对象,所以并不是同一个对象。直接返回了null。

代码:

package com.zbiti.java;

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

import org.junit.Test;

public class SetDemo {
    public static void main(String[] args) {

    }


    @Test
    public void test1(){
        Set<Dog> dogs = new HashSet<Dog>();
        Dog d1 = new Dog("white");
        Dog d2 = new Dog("white");
        dogs.add(d1);
        dogs.add(d2);


        System.out.println(dogs.contains(new Dog("white")));  //true
    }
}


class Dog{
    private String name;
    public Dog(String name) {
        this.name = name;
    }

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

    @Override
    public int hashCode() {
        return 1;
    }

    @Override
    public boolean equals(Object obj) {
        return true;
    }
}

结果:输出true
分析:桶里含有2个Dog对象,HashSet调用contains方法传进了一个新的Dog对象,由于Dog类里的hashcode、equals方法都被我们重写了,所以不管创建多少个对象,它们调用hashcode、equals方法的返回值都是一样的。根据HashMap的实现原理判断的3个原则,hash值相等,尽管对象不一样,但是equals返回值相等即说明桶里存在该对象。

代码:

package com.zbiti.java;

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

import org.junit.Test;

public class SetDemo {
    public static void main(String[] args) {

    }


    @Test
    public void test1(){
        Set<Dog> dogs = new HashSet<Dog>();
        Dog d1 = new Dog("white");
        Dog d2 = new Dog("white");
        dogs.add(d1);
        dogs.add(d2);


        System.out.println(dogs.contains(new Dog("white")));  //false
    }
}


class Dog{
    private String name;
    public Dog(String name) {
        this.name = name;
    }

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

    @Override
    public int hashCode() {
        return 1;
    }

    @Override
    public boolean equals(Object obj) {
        return false;
    }
}

结果:输出false
分析:Dog类hashcode()、equals()被我们重写,调用hashcode返回的值是相等的,由于对象不是同一个对象并且调用equals方法返回false,所以输出的结果为false。

代码:

package com.zbiti.java;

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

import org.junit.Test;

public class SetDemo {
    public static void main(String[] args) {

    }


    @Test
    public void test1(){
        Set<Dog> dogs = new HashSet<Dog>();
        Dog d1 = new Dog("white");
        Dog d2 = new Dog("white");
        dogs.add(d1);
        dogs.add(d2);


        System.out.println(dogs.contains(d1));
    }   //true
}


class Dog{
    private String name;
    public Dog(String name) {
        this.name = name;
    }

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

    @Override
    public int hashCode() {
        return 1;
    }

    @Override
    public boolean equals(Object obj) {
        return false;
    }
}

结果:输出true
分析:Dog类hashcode()、equals()被我们重写,调用hashcode返回的值是相等的,由于桶里的对象存在和将要放进桶里的对象是同一个对象,尽管调用equals方法返回false,结果的输出依然为true。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值