Set集合

来源:互联网 发布:程序员怀疑手游被抄袭 编辑:程序博客网 时间:2024/05/23 16:56

Set集合类的特点是不能有存在重复元素

Java库中的Set类有 HashSet, TreeSet, EnumSet, LinkedHashSet四种实现方式,其中前两种是比较常用的

一. HashSet(散列集)

根据前面的介绍,如果你存储的数据需要经常执行查询操作,那么你最好不要使用链表存储,因为链表不支持快速随机访问,如果要查找其中的元素只能逐个查找,这会十分痛苦。我们可以使用数组存储数据,应为数组结构支持快速随机访问,我们可以根据数组下标快速访问指定的元素。但是,还有一个问题,如果我们忘记了要查找元素的数组下标怎么办?为了解决这个问题,存在一种数据结构散列表(hash table)

散列表为每个对象计算出一个整数,这个整数称为散列码(hash code)。散列码的作用就相当于字典中的索引,它用来标记元素在散列表中的位置,方便快速查找,其遵循的原则是:不同的对象散列码一定不同,相同的对象一定相同。也就是说,如果 a.equals(b)为 true,则 a与 b必须具有相同的散列码。散列码具体怎么计算得出,可以由用户自定义,当然,散列码的算法也对散列表的查询效率有着关键作用

在 Java中,散列表用链表数组实现(一组链表首地址引用数组,数组每个元素都是一个链表的首地址)

这里写图片描述

在这其中,每个链表称为桶(bucket),要想查找表中对象的位置,就要先计算它的散列码,然后与桶的总数取余,所得到的结果就是保存这个元素的桶的索引。例如,某个对象的散列码为 76268,并且共有 128个桶,那么对象就应该保存在第 108个桶中,因为 76268 % 128 == 108

有时,会遇到桶被占满的情况,这种情况称为散列冲突(collision),在这种情况下,我们首先应该确认散列函数(散列码的算法)是适当合理的,如果不合理就会出现数据都倾向于某一个桶中。如果散列函数合理,那么就需要再散列(rehashed),也就是要新建一个更大的散列表,将数据存入新表,丢弃旧表。

通常,为保证散列表的运行性能,在创建初期就应该设置一个大致的桶数。通常,桶数设置为预计元素个数的 75%~150%,而且最好将桶数设置为素数。什么时候对散列表再散列由装填因子(load factor)决定,例如,如果装填因子设置为 0.75,那么当表中存储元素超过 75%时就会自动再散列

Java集合类库提供了一个 HashSet类,它实现了基于散列表的集。可以用 add方法添加元素。contains方法已经被重新定义,用来快速查看是否某个元素已经出现在集中。它只在某个桶中查找元素,而不必查看集合中的所有元素。

散列集迭代器将依次访问所有的桶,由于散列将元素分散在表的各个位置上,所以访问它们的顺序几乎是随机的。只有不关心集合中元素顺序时才应该使用HashSet


二. TreeSet(树集)

TreeSet类与 HashSet十分类似,但是,它比 HashSet有所改进。TreeSet是一个有序集合。可以将元素以任意顺序插入集合中,在对集合遍历时,每个元素将以排好的顺序输出。因此,迭代器总是以排好序的顺序访问每个元素。TreeSet排序是用树结构完成的(目前使用的是红黑树)

将一个元素添加到树中要比添加到散列表中慢,但是,与检查数组或链表中的重复元素相比还是快很多。


三. EnumSet(枚举集)

一种包含枚举类型值的集,其中的每个元素都是枚举类型或是其子类


四. LinkedHashSet

一种可以记住元素插入顺序的集

参考文献:[美]Cay S. Horsimann著 《Java 核心技术 卷I》(第十版)

原创粉丝点击