浅谈HashSet和HashCode

一.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中也只存放了一份对象。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,099评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,828评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,540评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,848评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,971评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,132评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,193评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,934评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,376评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,687评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,846评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,537评论 4 335
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,175评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,887评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,134评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,674评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,741评论 2 351

推荐阅读更多精彩内容

  • Java集合类可用于存储数量不等的对象,并可以实现常用的数据结构如栈,队列等,Java集合还可以用于保存具有映射关...
    小徐andorid阅读 1,931评论 0 13
  • 四、集合框架 1:String类:字符串(重点) (1)多个字符组成的一个序列,叫字符串。生活中很多数据的描述都采...
    佘大将军阅读 742评论 0 2
  • Java中的equals方法和hashCode方法是Object中的,所以每个对象都是有这两个方法的,有时候我们需...
    差不多先生_tl阅读 1,363评论 2 6
  • java.lang.Object类中有两个非常重要的方法: 1publicbooleanequals(Object...
    小陈阿飞阅读 676评论 1 1
  • 我们知道,计算机CPU和内存的交互是最频繁的,内存是我们的高速缓存区,用户磁盘和CPU的交互,而CPU运转速度越来...
    join_a922阅读 280评论 0 0