第八部分 集合 学习笔记

一、概述

1.集合有四种体系,Set、List、Queue、Map。
set无序不可重复
List有序可重复
Queue队列集合
Map映射关系
2.Java集合就像容器。将多个对象的引用扔进容器中。
3.为什么有集合类?为了保存数量不确定的数据,保存具有映射关系的数据(关联数组)
4.数组元素可以是基本类型值,也可以是对象的引用变量。
但是集合只能保存对象(对象的引用变量)。
5.集合主要两个接口是Collection和Map。它们是集合框架的根接口。

二、继承树概述

1.Collection集合类中,Set和List接口是Collection接口派生。Queue是Java提供的队列实现。
Set常用TreeSet、HashSet
Queue常用ArrayDeque
List常用ArrayList、LinkedList
2.Map常用HashMap
三、Collection和Iterator接口
1.Collection接口定义如下操作集合方法

add() addAll() clear() contains() containsAll() isEmpty() iterator()遍历
remove()删除包含的指定元素(仅限于第一个) removeAll() retainAll() int size()返回集合里元素个数
to Array()集合转化为数组

2.Iterator用于遍历集合
常用方法如下

1.hasNext()
2.next()
3.remove()
4.forEachRemaining() java8

Iterator it = new Iterator();
while(it.hasNext()){
条件
}

注意:Iterator仅限于Collection对象

三、set集合(分为HashSet类、LinkedHashSet类、TreeSet类、EnumSet类)

Set常用HashSet、TreeSet

1.HashSet:按Hash算法来存储集合元素,具有良好的存储和查询性能

  • 特点:无序线程不同步(两个或两个以上线程同时修改HashSet集合,必须使用代码保证其同步)。集合元素允许为null
  • HashSet判断两个元素是否相等,通过equals()方法比较两个数值是否相等hashCode()方法是否相等
  • 如果需要把某个类保存到HashSet集合中,重写这个类的equals()方法和hashCode()方法时,应该尽量保证两个对象通过equals()方法比较返回true时,也保证hashCode()返回值也相等。

提问:hashCode()方法对于HashSet是不是十分重要?

答:hash能保证快速查找被检索的对象,其价值在于速度。 当需要查询集合中某个元素时,hash算法可以直接根据该元素的hashcode值来计算出元素的存储位置,从而快速定位元素。
用数组来做对比,数组是所能存储一组元素里最快的数据结构。数组可以包含多个元素,每个元素都有索引,如果需要访问某个元素,只需要提供元素的索引,可以根据索引计算元素在内存中的存储位置。
HashSet没有索引,当程序向HashSet中添加元素时,使用hashCode来计算存储位置,可以定位到该元素。
数组的索引是连续的,数组的长度是固定的,无法自由增加数组的长度。而HashSet的长度可以通过HashCode自由增加。当直接访问HashSet的元素,会先调用计算出HashCode的值,然后直接到其对应的存储位置,取出元素。
这就是HashSet速度快的原因。

2.LinkedHashSet类【线程安全的】

  • HashSet的子类LinkedHashSet,其集合也根据元素的HashCode值来决定元素的存储位置。
  • 但它能够使用链表维护元素次序,使得元素看起来是以插入顺序保存的。
  • 当遍历LinkedHashSet数组时,其元素会按照添加顺序来输出。
  • 由于其需要维护插入顺序,使用链表维护内部顺序,于是它的性能略低于HashSet性能。

3.TreeSet类

  • TreeSet是SortedSet接口实现类,TreeSet可以确保集合元素出于排序状态
  • TreeSet提供额外方法:

comparator():采用定制排序,返回comparator。采用自然排序,返回null
first():第一个元素
last()
lower():返回小于指定数字的最大元素
higher()
subSet(start,end)截取
headSet(end)截取小于end之后的元素
tailSet(start)截取大于或等于start的元素

  • TreeSet并不是根据元素的插入顺序来进行排序的,是根据元素的实际大小进行排序。即升序排列
  • TreeSet采用红黑树的数据结构来存储集合元素。
  • TreeSet支持两种排序方法:自然排序定制排序

3.1.自然排序
TreeSet会使用compareTo()方法比较元素的大小关系
返回结果分为0 正整数 负整数。
0代表相等
正整数代表大于
负整数代表小于

延伸:使用了Comparable接口的常用类

BigDecimal、BigInteger
Character
Bollean
String
Date、Time

总结:如果希望TreeSet能正常运作,TreeSet只能添加一种类型的对象。
一旦改变了TreeSet即合理可变元素的实例变量,在试图删掉该对象时,会删除失败。

3.2.定制排序
非自然排序的排序,比如降序排序。通过Comparable接口。

4.EnumSet
转为枚举类设计的集合类。按定义顺序决定集合元素的顺序。
其内部以向量的形式存储,紧凑高效,所以其对象占用内存小,运行效率高。尤其是批量处理和执行速度很快。
其内部不允许插入null元素。如果插入null元素,会报NullPointerException异常。

各set实现类的性能分析

HashSet的性能比TreeSet性能好(添加、查询操作),因为TreeSet使用红黑树维护次序。如果只需要保存一种排序,才会使用TreeSet。
HashSet的子类LinkedHashSet比TreeSet慢,是因为链表的维护带来的额外开销。因为链表的存在,所以遍历LinkedHashSet速度会很快。
EnumSet是set实现类中性能最好的,她能保存同一枚举类的枚举值作为集合元素。
LinkedHashSet是线程安全的HashSet、TreeSet、EnumSet是线程不安全的,如果想要多个线程同时访问一个线程需要手动保证该集合的同步性。可通过Collections工具类的synchronizedSortedSet方法来包装Set集合,
如:
SortedSet s = Collection.synchronizedSortedSet(new TreeSet(…));

四、List集合-ArrayList和Vector

List集合是有序的、可重复的,集合中的每个元素都有其对应的顺序索引。
除Collection接口中的方法还有以下方法:
add
addAll
get
indexOf
lastIndexOf
remove
set
subList
replaceAll
sort

与Set的Iterator()方法不同,List有listIterator()其方法有:
hasPrevious():返回该迭代器关联的集合是否还有上一个元素
privious():返回迭代器的上一个元素
add()

1.ArrayList和Vector实现类

  • 这两个实现类是List两个典型实现,完全支持前面介绍的List接口全部功能。
  • ArrayList和Vector封装了一个动态的、允许再分配的Object[]数组。ArrayList和Vector对象使用initialCapacity参数来设置该数组的长度,当向ArrayList或Vector集合中添加元素超出了该数组的长度时,他们的initialCapacity自动增加
  • 一般情况下不需要考虑initialCapacity。但如果像ArrayList和Vector中添加大量元素时,可使用ensureCapacity方法一次性添加initialCapacity。减少分配次数,从而提升性能。
  • 如果创建空的ArrayList和Vector,不指定initialCapacity参数,那么Object()数组的长度默认为10。
  • ArrayList和Vector还提供两个方法来重新分配Object[]数组:

ensureCapacity(min):将数组长度增加或等于min的值
trimToSize():调整这两个集合的Object数组长度为当前元素的个数。可减少集合对象占用的内存空间。

ArrayList和Vector的用法几乎相同,但是由于Vector是一个古老的集合,所以其提供的方法名很长,其有很多缺点,尽量少使用。

  • ArrayList和Vector的区别是:
  • ArrayList是线程不安全的,当多个线程同时访问需要手动保证该集合的同步性。 Vector是线程安全的,所以性能相对较差。
  • 但即使需要List线程安全也不建议使用Vector实现类。
  • Vector还提供了Stack子类,用于模拟栈,先进后出LIFO
  • 最后push进栈的元素最先poo出栈。方法如下:

peek():返回栈的第一个元素,不pop出栈
pop():返回栈的第一个元素,并pop出栈
push():push进栈。最后一个进栈的元素位于栈顶。

3.Arrays工具类提供了方法asList(),可以把一个数组或一个指定个数的对象转换成一个List集合。是ArrayList实例。

五、Queue集合

用于模拟队列的数据结构,队列通常先进先出,队列头部保存存放时间最长的元素,队尾相反。
新插入的元素 offer到尾部,访问元素poll会返回队列头部元素
Queue有如下方法:
add():将指定元素插入到队尾
element():获得头部元素,不删除该元素
offer():将指定元素插入到尾部。当时用容量有限的队列时,此方法比add好。
peek():获取头部元素 ,不删除
poll():获取头部元素,删除,如果此队列是空的,返回null
remove():获取同步元素 并删除

Queue接口有PriorityQueue实现类Deque接口
Deque代表双端队列,可以从两端添加、删除元素,Deque的实现类可以当做队列使用,也可以当成来使用。

1.PriorityQueue实现类
是标准的队列实现类,按插入元素的大小进行排列。
违反了先进后出规则。
不允许插入null元素

两种排序方式:自然排序 定制排序
自然排序实现Comparable接口
定制排序传入Comparator对象,该对象负责队列中所有元素的排序

2.Deque接口与ArrayDeque
Deque是Qeque的子接口,他代表双端队列,Deque接口里定义了一些双端队列的方法,允许从两端操作队列元素

addFirst addLast descendingIterator() 双端队列迭代器,逆向顺序迭代 getFirst getLast
offerFirst offerLast peekFirst peekLast pollFirst pollLast pop push
相当于addFirst removeFirst removeLastOccurence

这里的peek都是不删 poll都是删 offer比add好

ArrayDeque可以当做队列使用,先进先出

3.LinkedList
可以根据索引来随机访问集合中的元素
LinkedList还实现了Deque接口,可以被当做双端队列使用,即被当做栈来使用,也可当队列使用。

  • LinkedList和ArrayList 、ArrayDeque的实现机制完全不同,
  • ArrayList、ArrayDeque内部已数组的形式来保存集合元素,因此随机访问集合元素时有较好的性能
  • LinkedList内部以链表的形式来保存元素,因此随机访问性能差,再插入删除时效果出色
  • Vector也是通过数组的形式来存储集合元素的,但是他实现了线程同步功能,所以性能差。

线性表性能分析

  • List接口的两种典型实现ArrayList和LinkedList:基于数组的线性表和基于链的线性表。
    Queue代表队列,Deque代表双端队列。
  • 一般来说数组是使用一块连续的内存区来保存所有的数组元素,所以数组的随机访问性能最好,所以内部以数组作为底层实现的集合在随机访问时性能都比较好。
    内部以链表作为底层实现的集合在插入和删除操作时有较好的性能。
  • 使用List的遍历建议: ArrayList、Vector集合是用随机访问方法get来遍历集合元素 LinkedList集合使用Iterator迭代器遍历集合元素 经常使用插入删除操作大量数据可使用LinkedList。
    如果有多个线程同时访问List集合,可以考虑Collection将集合包装成线程安全的集合。

【????????如何包装 】==>看第十部分

六、Map

Map是保存具有映射关系的数据,因此Map集合里保存着两组值,一组用来保存Map里的key,另一组用来保存Map里的value。 Map中的Key不允许重复,即同一个Map对象的两个任意的key通过equals方法比较总是返回false。
key没有顺序,不可以重复。

clear
containsKey
containsValue
entrySet 【entry是map的内部类】
get
isEmpty
keyset
put
putAll
remove
size
valuse

entry是map内部类,其特有三个方法:
getKey()
getValue()
setValue()

  • Java8新增方法P324
  • HashMap和HashTable实现类。
    HashMap和Hashtable都是Map接口的典型实现类。区别如下
  • Hashtable是一个线程安全的Map实现,HashMap线程不安全。HashMap性能更好。
    如果多个线程访问Map,Hashtalbe性能会更好。
  • Hashtable不允许使用null作为key和value,如果试图把null值放入Hashtable中,将会引发空指针异常。但是HashMap可以

LinkedHashMap实现类

  • LinkedHashMap是HaspMap的子类。利用双向链表维护kv对次序,负责维护map的迭代顺序。

  • 其可避免HashMap、Hashtable里的kv对进行排序,也可避免使用TreeMap增加的成本。

  • LinkedHashMap根据插入顺序维护表次序,从而性能低于HashMap

  • 又由于它以链表维护内部顺序,所以在迭代访问Map里的全部元素都有较好的性能。

使用Properties读写属性文件
Properties类是Hashtable类的子类,该对象处理文件很方便,他将Map文件中的k-v写入属性文件中,也可以吧属性文件的“属性名=属性值”加载到Map对象中。
属性文件里的属性名、属性值只能是字符串类型,所以Properties里的key、value都是字符串类型。P328

2.SortedMap接口和TreeMap实现类
Set接口派生SortedSet子接口,SortedSet接口有一个TreeSet实现类。
Map接口派生SortedMap子接口,SortedMap接口有一个TreeMap实现类。
TreeMap是一个红黑树数据结构,就没一个Key-value对作为红黑树的一个节点。TreeMap存储kV对需要根据键值对节点进行排序TreeMap可以保证所有键值对处于有序状态
TreeMap排序分为自然排序和定制排序。
自然排序实现Comparable接口,且所有key都是同一类对象,否则抛出ClassCastException。
定制排序:创建TreeMap时,传入一个Comparator对象,该对象负责TreeMap中的所有Key进行排序。采用定制排序不需要Map的key实现Comparable接口。

TreeMap判断两个Key相等的标准:两个key通过compareTo()方法返回0,则认为两个Key是相等的。

3.WeakHashMap实现类
WeakHashMap与HashMap用法基本相似。
其区别在于HashMap的key保留了对实际对象的强引用,这意味着只要该HashMap对象不被销毁,该HashMap的所有key所引用的对象就不会被垃圾回收,HashMap也不会自动删除那些Key所对应的键值对。
但是WeakHashMap保留了对实际对象的弱引用,如果key所引用的对象没有被其他强引用对象引用,key所引用的对象就会被垃圾回收,WeakHashMap也可能自动删除那些Key所对应的键值对。

4.IdentityHashMap实现类
这个Map的实现机制与HashMap基本相似。区别:
IdentityHashMap中,仅当Key1==Key2时,IdentityHashMap才认为两个Key相等。
对于HashMap而言,Key1和Key2通过equals()方法比较返回true,且他们的hashcode值相等才算两个key相等。
IdentityHashMap也是K和V可以为空。且不保证K-V对之间的顺序,更不保证随着时间推移他们的顺序不变。

5.EnumMap实现类
EnumMap是一个与枚举类一起使用的Map实现,EnumMap中的所有Key都必须是单个枚举类的枚举值。创建EnumMap时必须指定它对应的枚举类。特征如下

  • 内部以数组形式保存,紧凑高效
  • 根据Key的自然顺序(定义顺序)来维护键值对顺序。
  • 不允许key为null,允许value为null。

七、Map实现类的性能分析

  1. Map分为HashMap Hashtable LinkedHashMap TreeMap WeakHashMap
    IdentityHashMap EnumMap
  2. 其中Hashtable线程安全的,K和V不能为空。
  3. HashMap是线程不安全的,K和V可以为空,所以性能比Hashtable好。【一般应用场景程序应该多考虑HashMap】
  4. LinkedHashMap是HashMap的子类,维护链表插入顺序的次序,所以迭代遍历效率高,但总体性能低于HashMap。
  5. TreeMap是红黑树数据结构,会按键值对节点排序(自然排序和定制排序),总保持有序状态。
  6. WeakHashMap 的对象都保存为弱引用,会被垃圾回收。
  7. IdentityHashMap
    判定key相等是通过“==”。而HashMap一般使用equlas()和hashCode()判断是否为同一Key。
  8. EnumMap是枚举类型的Map。其内部以数组形式保存,允许value为null,key不可以。根据key的自然排序维护顺序。
  9. properties是Hashtable的子类,他的Key和Value都是String类型。

八、HashSet和HashMap性能

对于HashSet及其子类而言,hash算法用来决定集合元素的存储位置,并通过hash算法来控制集合大小,。对于HashMap和Hashtalbe而言,他们采用hash算法来决定Map中的key存储,并通过hash算法增加key集合的大小。
hash表里可以存储元素的位置被称为桶。hash算法可以根据hashcode值计算出桶的具体位置,但hash表的状态是open的,发生在hash冲突的情况下,单个桶可以存储多个元素,这些元素以链表形式存储。因此HashMap和Hashtable使用Hash算法只考虑Key。

因此HashSet、HashMap的hash表包含如下属性:
容量(Capacity):桶的数量
初始化容量(initial Capacity)
尺寸(size):hash表中记录的数量
负载因子(load factor):尺寸÷容量
负载极限决定hash表的最大填满程度

如果从一开始就知道要使用HashSet HashMap Hashtable会包含大量初始化数据 ,可以使用较大的初始化热容量,如果吃书画容量始终大于HashSet HashMap Hashtable所包含的最大记录数÷负载极限,就不会发生“rehashing”。

九、操作集合的工具类Collection

【1.排序操作 2.同步控制 3.查找、替换操作. 4.设置不可变集合】

1.排序操作
reverse:反转
shuffle:洗牌
sort:升序排序
swap(List list ,int i,int j):交换位置
rotate(List list ,int distance):distance为整数,将list集合后distance个元素整体移动到前面。
负数,移动到后面。

十、同步控制

Collections类提供了多个synchronizedXxx()方法,该方法可以将指定集合包装成线程同步的集合,从而可以解决多线程并发访问集合时的线程安全问题。
从Set中的HashSet TreeSet
List中的 ArrayList 、LinkedList
Queue中的 ArrayDeque
Map中的HashMap和TreeMap都是现成不安全的

Collection c = new Collections.synchronizedCollection(new ArrayList());
List list = new Collection.synchronizedList(new ArrayList());
Set set = new Collection .synchronizedSet(new HashSet());
Map m = new Collection.synchronizedMap(new HashMap());

十一、设置不可变集合

Collections提供了三种方法来返回不可变集合。

  1. emptyXxx():返回一个空的不可变得集合对象。集合类型:list、SortedSet、Set、Map、SortedMap等
  2. singletonXxx():List或Map类型的,返回一个值包含某种指定对象的不可变的集合对象
  3. unmodifiableXxx():返回指定集合对象的不可变视图。可以是Map、SortedMap等

十二、繁琐的接口:Enumeration

只能用来遍历Vector、Hashtable这种古老的集合。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值