码迷,mamicode.com
首页 > 其他好文 > 详细

HashSet保证元素唯一性的代码体现(源码和内部图 进行解析)

时间:2015-07-05 00:52:19      阅读:116      评论:0      收藏:0      [点我收藏+]

标签:

技术分享

---------------------------------------------------------------

测试类:

/*
 * 需求:存储自定义对象,并保证元素的唯一性
 * 要求:如果两个对象的成员变量值都相同,则为同一个元素。
 *
 * 目前是不符合我的要求的:因为我们知道HashSet底层依赖的是hashCode()和equals()方法。
 * 而这两个方法我们在学生类中没有重写,所以,默认使用的是Object类。
 * 这个时候,他们的哈希值是不会一样的,根本就不会进行后续的equal()判断,从而直接执行了添加操作。
 */
public class HashSetDemo2 {
    public static void main(String[] args) {
        // 创建集合对象
        HashSet<Student> hs = new HashSet<Student>();

        // 创建学生对象
        Student s1 = new Student("林青霞", 27);
        Student s2 = new Student("柳岩", 22);
        Student s3 = new Student("王祖贤", 30);
        Student s4 = new Student("林青霞", 27);
        Student s5 = new Student("林青霞", 20);
        Student s6 = new Student("范冰冰", 22);

        // 添加元素
        hs.add(s1);
        hs.add(s2);
        hs.add(s3);
        hs.add(s4);
        hs.add(s5);
        hs.add(s6);

        // 遍历集合
        for (Student s : hs) {
            System.out.println(s.getName() + "---" + s.getAge());
        }
    }
}

 

---------------------------------------------------------------

具体类代码:

package cn.itcast_02;

/**
 * @author Administrator
 *
 */
public class Student {
    private String name;
    private int age;

    public Student() {
        super();
    }

    public Student(String name, int age) {
        super();
        this.name = name;
        this.age = 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;
    }

    @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 (getClass() != obj.getClass())
            return false;
        Student other = (Student) 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;
    }

    // @Override
    // public int hashCode() {
    // // return 0;
    // // 因为成员变量值影响了哈希值,所以我们把成员变量值相加即可
    // // return this.name.hashCode() + this.age;
    // // 看下面
    // // s1:name.hashCode()=40,age=30
    // // s2:name.hashCode()=20,age=50
    // // 尽可能的区分,我们可以把它们乘以一些整数
    // return this.name.hashCode() + this.age * 15;
    // }
    //
    // @Override
    // public boolean equals(Object obj) {
    // // System.out.println(this + "---" + obj);//这个位置可以查看究竟hash码相同时,在同一数组元素(哈希码相同即数组元素相同,数组元素下包含一条链表)
    // if (this == obj) {      //究竟对象比较了多少次,因为hash码相同,才会到相同hash码的地方进行比较,而且通过查看源码,是从最后添加的元素开始比较
    // return true;            //比较方法为自己对具体类的重写方法(因为不重写的话,就会继承父类Object方法,直接比较对象地址),而重写之后能以对象成员作为比较  
    // }                           //表明对象成员相同,就说明两个内容一致。
    //
    // if (!(obj instanceof Student)) {
    // return false;
    // }
    //
    // Student s = (Student) obj;
    // return this.name.equals(s.name) && this.age == s.age;
    // }
    //
    // @Override
    // public String toString() {
    // return "Student [name=" + name + ", age=" + age + "]";
    // }

}
----------------------------------------------------------------

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()方法相关这里调用的就是具体添加类的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))) {//只要这里e.hash == hash为TRUE,说明两个比较量可能会在同一个数组链表下,
 V oldValue = e.value;                                                 //此时,(k = e.key) == key一般都是False的,因为这两个的地址一般都是不相等的
  e.value = value;                                                        //为了不让相同元素进入该哈希表,只能动用最后防线:具体类的equal()方法,重写后的
 e.recordAccess(this);                                                 //方法比较成员变量的值,相同则不写入HashSet进行存储,因为是||,只要一个true就能
  return oldValue;                                                       //执行if语句块内容return,阻挡对象写入哈希表中,若equal方法为false,则&&也为false
                //走这里其实是没有添加元素                       //最后也只能执行对象addEntry(hash, key, value, i);进行插入
            }
        }

        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);
    }
}


hs.add("hello");
hs.add("world");
hs.add("java");
hs.add("world");

 

 

-----------------------------------------------------------------

HashSet保证元素唯一性的代码体现(源码和内部图 进行解析)

标签:

原文地址:http://www.cnblogs.com/canceler/p/4621508.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!