【Java基础】集合总结(二)——Set集合、List集合

一、Set集合

Set接口是Collection的子接口,Set接口没有提供额外的方法。

Set集合不能记住元素的添加顺序,不允许包含重复元素。如果试图把两个相同的元素加入同一个Set集合中,则添加操作失败,add()方法返回false,且新元素不会被加入。

(一)HashSet类

1、HashSet按Hash算法来存储集合中的元素,因此具有很好的存取和查找性能。

2、特点:

  • 不能保证元素的排列顺序,顺序可能与添加顺序不同,顺序也有可能发生变化。
  • HashSet不是同步的。如果多个线程同时访问一个HashSet,假设有两个或者两个以上线程同时修改了HashSet集合时,则必须通过代码来保证其同步。
  • 集合元素值可以是null。

3、HashSet集合判断两个元素相等的标准:

       两个对象通过equals()方法比较相等,并且两个对象的hashCode()方法返回值也相等。

4、当把一个对象放入HashSet中时,如果需要重写该对象对应类的equals()方法,则也应该重写其hashCode方法。

       规则是:如果两个对象通过equals()方法比较返回true,这两个对象的hashCode值也应该相同。

5、向HashSet中添加元素的过程:

  • 当向 HashSet 集合中存入一个元素时, HashSet 会调用该对象的 hashCode() 方法来得到该对象的 hashCode 值, 然后根据 hashCode 值, 通过某种散列函数决定该对象在 HashSet 底层数组中的存储位置。 ( 这个散列函数会与底层数组的长度相计算得到在数组中的下标, 并且这种散列函数计算还尽可能保证能均匀存储元素, 越是散列分布,该散列函数设计的越好)
  • 如果两个元素的hashCode()值相等, 会再继续调用equals方法, 如果equals方法结果为true, 添加失败; 如果为false, 那么会保存该元素, 但是该数组的位置已经有元素了,那么会通过链表的方式继续链接。
  • 如果两个元素的 equals() 方法返回 true,但它们的 hashCode() 返回值不相等, hashSet 将会把它们存储在不同的位置,但依然可以添加成功。

6、重写 hashCode() 方法的基本原则:

  • 在程序运行时,同一个对象多次调用 hashCode() 方法应该返回相同的值。
  • 当两个对象的 equals() 方法比较返回 true 时,这两个对象的 hashCode()方法的返回值也应相等。
  • 对象中用作 equals() 方法比较标准的实例变量,都应该用来计算 hashCode 值。

(二)LinkedHashSet类

1、LinkedHashSet 是 HashSet 的子类。

2、LinkedHashSet 根据元素的 hashCode 值来决定元素的存储位置,但它同时使用双向链表维护元素的次序,这使得元素看起来是以插入的顺序保存的。(输出LinkedHashMap集合元素时,元素的顺序总是与添加的顺序一致)

3、LinkedHashSet需要维护元素的插入顺序,因此性能略低于 HashSet, 但在迭代访问 Set 里的全部元素时有很好的性能。(对于频繁的遍历操作,LinkedHashSet效率高于HashSet)

4、LinkedHashSet 不允许集合元素重复。

(三)TreeSet类

1、TreeSet 是 SortedSet 接口的实现类, TreeSet 可以确保集合元素处于排序状态。

2、与HashSet集合相比,TreeSet还提供了如下几个额外的方法:

Comparator comparator():如果TreeSet采用了定制排序,则该方法返回定制排序所使用的Compaeator;如果TreeSet采用了自然排序,则返回null
Object first():返回集合中的第一个元素
Object last():返回集合中的最后一个元素
Object lower(Object e):返回集合中位于指定元素之前的元素
Object higher(Object e):返回集合中位于指定元素之后的元素
SortedSet subSet(Object fromElement, Object toElement):返回此Set的子集合,范围从fromElement(包含)到toElement(不包含)
SortedSet headSet(Object toElement):返回此Set的子集合,由小于toElement的元素组成
SortedSet tailSet(Object fromElement):返回此Set的子集合,由大于或等于toElement的元素组成

3、TreeSet底层使用红黑树的数据结构来存储集合元素。

参考: http://www.cnblogs.com/yangecnu/p/Introduce-Red-Black-Tree.html

 

4、排序规则

TreeSet支持两种排序方法:自然排序和定制排序。默认情况下, TreeSet 采用自然排序。

  • 自然排序中,比较两个对象是否相同的标准为:compareTo(Object obj) 方法比较是否返回0。
  • 定制排序中,比较两个对象是否相同的标准为:compare(Object obj) 方法比较是否返回0。

(四)EnumSet类

1、EnumSet是一个专为枚举类设计的集合类,EnumSet中的所有元素都必须是指定枚举类型的枚举值。

2、EnumSet的集合元素是有序的,EnumSet以枚举值在Enum类内的定义顺序来决定集合元素的顺序。

3、EnumSet在内部以位向量的形式存储,这种存储形式非常紧凑、高效,因此EnumSet对象占用内存很小,而且运行效率很好,尤其是批量操作。

4、EnumSet集合不允许加入null元素。

(五)各Set实现类的性能比较

HashSet的性能总是比TreeSet好(特别是最常用的添加、查询元素等操作),因为TreeSet需要额外的红黑树算法来维护集合元素的次序。只有当需要一个保持排序的Set时,才应该使用TreeSet,否则都应该使用HashSet。

对于普通的插入、删除操作,LinkedHashSet比HashSet要略微慢一点,这是由维护连表所带来的额外开销造成的,但由于有了链表,遍历LinkedHashMap会更快。

EnumSet是所有Set实现类中性能最好的,但它只能保存同一个枚举类的枚举值作为集合元素。

Set的三个实现类HashSet、TreeSet和EnumSet都是线程不安全的。为了避免出现线程同步问题,可在Set集合创建时,通过Collection工具类的synchronizedSortedSet方法来“包装”该Set集合。

二、List集合

List集合代表一个元素有序、可重复的集合,集合中每个元素都有其对应的顺序索引。

List集合允许使用重复元素,可以通过索引来访问指定位置的集合元素。List集合默认按元素的添加顺序设置元素的索引。

JDK API中List接口的实现类常用的有: ArrayList、 LinkedList和Vector。

常用方法:

void add(int index, Object ele):在index位置插入ele元素
boolean addAll(int index, Collection eles):从index位置开始将eles中的所有元素添加进来
Object get(int index):获取指定index位置的元素
int indexOf(Object obj):返回obj在集合中首次出现的位置
int lastIndexOf(Object obj):返回obj在当前集合中末次出现的位置
Object remove(int index):移除指定index位置的元素,并返回此元素
Object set(int index, Object ele):设置指定index位置的元素为ele
List subList(int fromIndex, int toIndex):返回从fromIndex到toIndex位置的子集合

(一)ArrayList类

1、ArrayList 是 List 接口的典型实现类、主要实现类。ArrayList是线程不安全的,效率高。

2、本质上, ArrayList是对象引用的一个”变长”数组。

3、ArrayList的JDK1.8之前与之后的实现区别?

  • JDK1.7: ArrayList像饿汉式,直接创建一个初始容量为10的数组。
  • JDK1.8: ArrayList像懒汉式,一开始创建一个长度为0的数组,当添加第一个元素时再创建一个始容量为10的数组。

4、Arrays.asList(Object … a) 方法可以把一个数组或指定个数的对象转换成一个固定长度的List集合,这个List集合既不是ArrayList实现类的实例,也不是Vector实现类的实例,而是Arrays的内部类ArrayList的实例。

(二)LinkedList类

1、对于频繁的插入或删除元素的操作,建议使用LinkedList类,效率较高。

2、LinkedList: 双向链表, 内部没有声明数组,而是定义了Node类型的first和last,用于记录首末元素。同时,定义内部类Node,作为LinkedList中保存数据的基本结构。 Node除了保存数据,还定义了两个变量:

  • prev变量记录前一个元素的位置
  • next变量记录下一个元素的位置

(三)Vector类

1、Vector 是一个古老的集合, JDK1.0就有了。大多数操作与ArrayList相同,区别之处在于ArrayList是线程不安全的,而Vector是线程安全的,效率低。

2、在各种list中,最好把ArrayList作为缺省选择。当插入、删除频繁时,使用LinkedList;因为Vector是线程安全的,所以Vector的性能比ArrayList的性能要低,应尽量避免使用。

(四)ArrayList/LinkedList/Vector的异同?

1、同:三个类都是实现了List接口,存储数据的特点相同:存储有序的、可重复的数据。

2、ArrayList和LinkedList的异同:

二者都线程不安全,相对线程安全的Vector,执行效率高。

此外, ArrayList是实现了基于动态数组的数据结构, LinkedList基于链表的数据结构。对于随机访问get和set, ArrayList觉得优于LinkedList,因为LinkedList要移动指针。对于新增和删除操作add(特指插入)和remove, LinkedList比较占优势,因为ArrayList要移动数据。

3、ArrayList和Vector的区别:

Vector和ArrayList几乎是完全相同的,唯一的区别在于Vector是同步类(synchronized),属于强同步类。因此开销就比ArrayList要大,访问要慢。正常情况下,大多数的Java程序员使用ArrayList而不是Vector,因为同步完全可以由程序员自己来控制。 Vector每次扩容请求其大小的2倍空间,而ArrayList是1.5倍。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值