美文网首页android
浅谈HashSet和HashCode

浅谈HashSet和HashCode

作者: CZ_WL | 来源:发表于2020-03-23 11:17 被阅读0次

    一.HashSet

    Kotlin中 ==HashSet==是一个集合类,它扩展了==AbstractMutableSet==类并实现了==Set==接口。 ==HashSet==类使用散列机制存储元素。 它支持读写功能。 ==但它不支持重复值==,也不保证元素的顺序。

    HashSet申明:

    open class HashSet<E> : AbstractMutableSet<E> (source)
    

    HashSet构造函数:

    构造函数 描述
    HashSet() 构造一个空的HashSet实例
    HashSet(initialCapacity:Int,loadFactor:Float = 0f) 构造一个指定容量的HashSet实例
    HashSet(elements: Collection<E>) 使用指定的集合元素构造HashSet实例

    HashSet成员函数

    函数 描述
    open fun add(element: E): Boolean 如果此 set 中尚未包含指定元素,则添加指定元素;如果此 set 已包含该元素,则该调用不更改 set 并返回 false
    open operator fun contains(element: E): Boolean 它检查当前集合中是否存在指定的元素。
    open fun isEmpty(): Boolean 它检查当前集合是否为空(不包含任何元素)。 如果找到的集合为空则返回true,否则返回false
    open fun iterator():MutableIterator<E> 它返回当前对象元素的迭代器。
    open fun remove(element: E): Boolean 如果当前集合中存在,它将删除提及元素。 如果删除成功则返回true,则返回false
    open fun clear() 它会删除此集合中的所有元素。

    HashSet属性

    函数 描述
    open val size: In 此属性用于返回HashSet集合的大小。

    点进代码可以发现Kotlin中的HashSet其实就是借用了java.util.HashSet

    public actual typealias HashSet<E> = java.util.HashSet<E>
    

    一道关于HashSet的Java笔试题:
    对于一个字符串,请设计一个高效算法,找到第一次重复出现的字符。
    给定一个字符串(不一定全为字母)A及它的长度n。请返回第一个重复出现的字符。保证字符串中有重复字符,字符串的长度小于等于500。
    测试样例:
    "eredfdad",8
    返回:e
    代码:

    public class FirstReport {
        public static char findFirstRepeat(String A, int n) {
            char[] a=A.toCharArray();
            HashSet hashSet = new HashSet();
            for (int i = 0; i < n; i++) {
                if(!hashSet.add(a[i])){   //如果加入失败 返回false
                    return a[i];
                    }
            }
            return 0;
        }
    
        public static void main(String... args) {
            System.out.println(findFirstRepeat("eredfdad",8));
        }
    }
    

    输出:e 说明了集合中只能够存放唯一的对象,也就是相同的对象只会存放一个。

    二.hashCode和equals()方法

    哈希表这个数据结构想必大多数人都不陌生,而且在很多地方都会利用到hash表来提高查找效率。在Java的Object类中有一个方法:

    public native int hashCode();
    

    关于HashCode的官方文档定义:

    hashcode方法返回该对象的哈希码值。支持该方法是为哈希表提供一些优点,
    例如,java.util.Hashtable 提供的哈希表。hashCode 的常规协定是: 
    在 Java 应用程序执行期间,在同一对象上多次调用 hashCode 方法时,必须一致地返回相同的整数,前提是对象上 equals 比较中所用的信息没有被修改。从某一应用程序的一次执行到同一应用程序的另一次执行,该整数无需保持一致。 
    如果根据 equals(Object) 方法,两个对象是相等的,那么在两个对象中的每个对象上调用 hashCode 方法都必须生成相同的整数结果。 
    

    对于包含容器类型的程序设计语言来说,基本上都会涉及到hashCode。在Java中也一样,hashCode方法的主要作用是为了配合基于散列的集合一起正常运行,这样的散列集合包括HashSetHashMap以及HashTable
     
      为什么这么说呢?考虑一种情况,当向集合中插入对象时,如何判别在集合中是否已经存在该对象了?(注意:集合中不允许重复的元素存在)
    也许大多数人都会想到调用equals方法来逐个进行比较,这个方法确实可行。但假如有很多条数据的话,如果采用equals方法去逐一比较,效率必然是一个问题。
    此时hashCode方法的作用就体现出来了,当集合要添加新的对象时,先调用这个对象的hashCode方法,得到对应的hashcode值,实际上在HashMap的具体实现中会用一个table保存已经存进去的对象的hashcode值,如果table中没有该hashcode值,它就可以直接存进去,不用再进行任何比较了;如果存在该hashcode值, 就调用它的equals方法与新元素进行比较,相同的话就不存了,不相同就散列它的地址,所以这里存在一个冲突解决的问题,学过数据结构的应该想到,解决冲突的方法常用的有:双散列函数探查法平方探测法等,这样实际调用equals方法的次数就大大降低了,说通俗一点:Java中的hashCode方法就是根据一定的规则将与对象相关的信息(比如对象的存储地址,对象的字段等)映射成一个数值,这个数值称作为散列值。

     假如内存中有这样的位置:
     0 1 2 3 4 5 6 7 
     将带有ID的字段的类存放进内存 我有个类,类里有字段叫做id,要把它放进这个内存中之 如果你不 
     用hashcode,而是乱放的话,那么当查找时就需要到这八个位置里挨个去找,或者用一些查
     找的算法。但如果用hashcode那就会使效率提高很多。
     假设定义hashcode=ID%8,比如有一个ID为9,9除8余1,把此类放在1的位置,查找该类时,就通过
     该类的ID%8来获取存放的地址。
     但是如果两个类有相同的hashcode怎么办那(我们假设上面的类的ID不是唯一的),例如9除以8和
     17除以8的余数都是1,存放的时候通过解决冲突的方法存放,但是依然会有不同的ID的类被放到一起,
     这时候查找的时候就需要 equals来确定此对象是否是你想要的对象。所以hashcode()和equals()需要
     一起重写。你需要先通过hashcode找到对象,再用equals判断对象。
    
    

    来看一段代码:

    class Person(var age:Int,var name:String){
        override fun hashCode(): Int {
            return age%7
        }
    
    }
    
    fun main() {
    
        val persons = HashSet<Person>()
        val a=Person(20,"CZ")
        val b=Person(20,"CZ")
        persons.add(a)
        persons.add(b)
        println(a.hashCode()==b.hashCode())
        println(persons.size)
        println(persons)
    }
    
    输出:
    true
    2
    [com.czwl.kotlin.types.eg.Person@6, com.czwl.kotlin.types.eg.Person@6]
    

    此时没有复写equals,只是重写了hashcode方法,于是会调默认的方法,将a,b 放入HashSet中,但HashSet只能够存放唯一的对象,也就是相同(适用于equals方法)的对象只会存放一个,但是这里实际上是2个相同的都被放到了HashSet中,这样HashSet就失去了他本身的意义了。
    此时再重写equals方法:

    class Person(var age:Int,var name:String){
        override fun hashCode(): Int {
            return age%7
        }
        override fun equals(other: Any?): Boolean {
            val other=(other as?Person)?:return false //判断是否为Person 
            //是的话强转为Person
            //不是返回false
            return other.age==age&&other.name==name
        }
    }
    
    fun main() {
    
        val persons = HashSet<Person>()
        val a=Person(18,"CZ")
        val b=Person(18,"CZ")
        persons.add(a)
        persons.add(b)
        println(a.hashCode()==b.hashCode())
        println(persons.size)
        println(persons)
    }
    
    
    输出:
    true
    1
    [com.czwl.kotlin.types.eg.Person@6]
    

    现在两个对象就完全相等了,HashSet中也只存放了一份对象。

    相关文章

      网友评论

        本文标题:浅谈HashSet和HashCode

        本文链接:https://www.haomeiwen.com/subject/fmgiyhtx.html