文章目录
- 集合框架
- Collection
- List
- List常用方法
- ArrayList
- ArrayList常用方法
- LinkedList
- LinkedList常用方法
- Vector
- Vector 常用方法
- Stack
- Stack 常用方法
- Set
- HashSet
- HashSet 常用方法
- LinkedHashSet
- LinkedHashSet 常用方法
- TreeSet
- TreeSet常用方法
- EnumSet
- EnumSet 常用方法
- Map
- HashMap
- HashMap 常用方法
- Hashtable
- LinkedHashMap
- LinkedHashMap 的常用方法
- TreeMap
- TreeMap 常用方法
- TreeMap 排序方式
- WeakHashMap
- WeakHashMap 常用方法
- IdentityHashMap
- IdentityHashMap 常用方法
- EnumMap
- EnumMap 常用方法
- Properties
- Properties 常用方法
集合框架
Collection
Collection 接口用于表示任何对象或元素组。它是 List 和 Set 的父接口。
Collection 声明的方法:
点击查看方法摘要
List
List集合代表一个有序、可变长、可重复的集合,集合中每个元素都有其对应的顺序索引。List集合默认按照元素的添加顺序设置元素的索引,可以通过索引(类似数组的下标)来访问指定位置的集合元素。
List相当于下图的格子,按顺序放入值,for循环可以挨个取出:
实现List接口的集合主要有:ArrayList、LinkedList、Vector、Stack。
特点梳理:
-
List集合为列表类型,以线性方式,有序存取对象。各元素的顺序就是对象插入时的顺序,即元素按进入先后有序保存。
-
List集合有序是指存储的元素有下标,下标从0开始,以1递增。所以可以通过使用索引来访问List集合中的元素,即可以用下标进行元素的操作
-
List集合中的元素允许重复,即可以存储重复的元素。
List常用方法
点击查看方法摘要
ArrayList
ArrayList是一个动态数组,也是我们最常用的集合,是List类的典型实现。它允许任何符合规则的元素插入甚至包括null。每一个ArrayList都有一个初始容量(10),该容量代表了数组的大小。随着容器中的元素不断增加,容器的大小也会随着增加。在每次向容器中增加元素的同时都会进行容量检查,当快溢出时,就会进行扩容操作。所以如果我们明确所插入元素的多少,最好指定一个初始容量值,避免过多的进行扩容操作而浪费时间、效率。
如上图,ArrayList若要找Killer,则要通过for循环,从Tom开始挨个找才能找到,ArrayList的查询速度比LinkedList快,但不及HashMap快。数据若是很庞大,而且又频繁查找的业务,建议使用散列表。
特点梳理:
-
底层数据结构是数组,添加、查找、删除均采用基本数组操作,可以存储重复元素
-
查找效率高,添加、删除效率低,整体效率高
-
初始大小10,最大容量Integer.MAX_VALUE - 8
-
满时扩容,扩容大小为1.5倍,(int newCapacity = oldCapacity + (oldCapacity >> 1)。
-
采用Arrays.copyOf()产生新数组
-
没有同步,线程不安全
ArrayList常用方法
点击查看方法摘要
LinkedList
LinkedList 是List 接口的另一个实现类,除了可以根据索引访问集合元素外,LinkedList 还实现了 Deque 接口,可以当作双端队列来使用,也就是说,既可以当作“栈”使用,又可以当作队列使用。
LinkedList 的实现机制与 ArrayList 的实现机制完全不同,ArrayList 内部以数组的形式保存集合的元素,所以随机访问集合元素有较好的性能;LinkedList 内部以链表的形式保存集合中的元素,所以随机访问集合中的元素性能较差,但在插入删除元素时有较好的性能。
特点梳理:
-
同时实现了Queue和Deque接口
-
底层采用双向链表实现,头结点不存放数据,允许为null
-
只要是遍历链表就需要使用 ListIterator
-
索引优化,可以根据 index 可以选择从 first 和 last 开始遍历
-
线程不安全
LinkedList常用方法
点击查看方法摘要
Vector
与 ArrayList 相似,但是 Vector 是同步的。所以说 Vector 是线程安全的动态数组。它的操作与 ArrayList 几乎一样。
Vector 类实现了可扩展的对象数组。 像数组一样,它包含可以使用整数索引访问的组件。 但是, Vector 的大小可以根据需要增长或缩小,以适应在创建 Vector 之后添加和删除项目。
特点梳理:
-
底层由数组实现,添加、查找、删除均采用基本数组操作,所以查找效率高,添加、删除效率低
-
初始大小10,最大容量 Integer.MAX_VALUE - 8
-
满时扩容,扩容大小为1.5倍,(int newCapacity = oldCapacity + (oldCapacity >> 1)
-
采用 Arrays.copyOf() 产生新数组
-
线程不安全
Vector 常用方法
点击查看方法摘要
Stack
Stack 继承自 Vector,实现一个后进先出的堆栈。Stack 提供5个额外的方法使得 Vector 得以被当作堆栈使用。基本的 push 和 pop 方法,还有 peek 方法得到栈顶的元素,empty 方法测试堆栈是否为空,search 方法检测一个元素在堆栈中的位置。Stack 刚创建后是空栈。
Stack 常用方法
点击查看方法摘要
Set
继承自 Collection 接口,且方法相同,Set 没有在 Collection 的基础上进行功能的扩展,只是比Collection 接口更加严格了,Set 接口是无序的。
Set集合的实现类可说是基于Map集合去写的。通过内部封装Map集合来实现的比如HashSet内部封装了HashMap。
Set相当于往袋子里放东西,无序取出(使用迭代器取出),如下图:
特点梳理:
-
继承自Collection
-
Set集合中的元素无序,即插入无序
-
不可指定位置访问,即没有索引值
HashSet
HashSet是Set集合最常用实现类,是其经典实现。HashSet是按照hash算法来存储元素的,因此具有很好的存取和查找性能。
HashSet使用HashMap的数据结构,仅使用HashMap中的key结构,value值一律为null。也就是说真实数据存储在 HashMap 的 key 列,value 列全部为空。
HashSet 存储原理:
当向 HashSet 集合存储一个元素时,HashSet 会调用该对象的 hashCode() 方法得到其 hashCode 值,然后根据 hashCode 值决定该对象的存储位置,
HashSet 集合判断两个元素相等的标准是:
- 两个对象通过 equals() 方法比较返回 true
- 两个对象的hashCode()方法返回值相等。
因此,如果(1)和(2)有一个不满足条件,则认为这两个对象不相等,可以添加成功。如果两个对象的 hashCode() 方法返回值相等,但是两个对象通过 equals() 方法比较返回 false,HashSet会以链式结构将两个对象保存在同一位置,这将导致性能下降,因此在编码时应避免出现这种情况。
HashSet 查找原理:
基于 HashSet 以上的存储原理,在查找元素时,HashSet 先计算元素的 HashCode 值(也就是调用对象的 hashCode 方法的返回值),然后直接到 hashCode 值对应的位置去取出元素即可,这就是 HashSet 速度很快的原因。
重写 hashCode() 方法的基本原则:
- 在程序运行过程中,同一个对象的hashCode()方法返回值应相同。
- 当两个对象通过equals()方法比较返回true时,这两个对象的hashCode()方法返回值应该相等。
- 对象中用作equals()方法比较标准的实例变量,都应该用于计算hashCode值。
特点梳理:
-
不能保证元素的顺序
-
HashSet不是线程同步的,如果多线程操作HashSet集合,则应通过代码来保证其同步
-
集合元素值可以是null
HashSet 常用方法
点击查看方法摘要
LinkedHashSet
LinkedHashSet 是 HashSet 的一个子类,具有 HashSet 的特性,也是根据元素的 hashCode 值来决定元素的存储位置。但它使用链表维护元素的次序,元素的顺序与添加顺序一致。由于 LinkedHashSet 需要维护元素的插入顺序,因此性能略低于 HashSet,但在迭代访问 Set 里的全部元素时有很好的性能。
LinkedHashSet 是一个非线程安全的集合。如果有多个线程同时访问当前 LinkedHashSet 集合容器,并且有一个线程对当前容器中的元素做了修改,那么必须要在外部实现同步保证数据的冥等性。
特点梳理:
-
LinkedHashSet的底层使用LinkedHashMap存储元素
-
LinkedHashSet是有序的,它是按照插入的顺序排序的
-
LinkedHashSet 是一个非线程安全的集合
LinkedHashSet 常用方法
点击查看方法摘要
TreeSet
TreeSet 是 SortedSet 接口的实现类,TreeSet可以保证元素处于排序状态,它采用红黑树的数据结构来存储集合元素。TreeSet 支持两种排序方法:自然排序和定制排序,默认采用自然排序。
排序:
当向TreeSet中添加自定义对象时,有 2 种排序方法:
- 自然排序
要求自定义类实现 java.lang.Comparable 接口并重写 compareTo(Object obj) 方法。在此方法中,指明按照自定义类的哪个属性进行排序
- 定制排序
当元素不具备比较性时,此时只能为容器添加比较器来解决问题,即创建 TreeSet 时向其中加入Comparator
特点梳理:
-
有序
-
不可重复
-
红黑树
-
基于Treemap实现
-
自定义排序等特点
-
非线程安全
-
TreeSet中存储的类型必须是一致的,不能一下存 int,一下又存 String
TreeSet常用方法
点击查看方法摘要
EnumSet
EnumSet 是一个专为枚举类设计的集合抽象类,EnumSet 中的所有元素都必须是指定枚举类型的枚举值,该枚举类型在创建EnumSet时显式或隐式地指定。不允许添加 null 值。EnumSet 的集合元素也是有序的,它以枚举值在 Enum 类内的定义顺序来决定集合元素的顺序。
特点梳理:
-
EnumSet的集合元素也是有序的,EnumSet以枚举值在Enum类内的定义顺序来决定集合元素的顺序
-
EnumSet在内部以位向量的形式存储,这种存储形式非常紧凑、高效,因此 EnumSet 对象占用内存很小,而且运行效率很好。尤其是进行批量操作(如调用containsAll()和retainAll()方法)时,如果其参数也是EnumSet集合,则该批量操作的执行速度也非常快
-
EnumSet 集合不允许加入 null 元素,如果试图插入 null 元素,EnumSet 将抛出 NullPointerException 异常
-
EnumSet 类没有暴露任何构造器来创建该类的实例,程序应该通过它提供的类方法来创建 EnumSet 对象
-
如果只是想判断 EnumSet 是否包含 null 元素或试图删除 null 元素都不会抛出异常,只是删除操作将返回false,因为没有任何 null 元素被删除。
EnumSet 常用方法
点击查看方法摘要
Map
Map 接口采用键值对 Map<K,V> 的存储方式,保存具有映射关系的数据,因此,Map 集合里保存两组值,一组值用于保存 Map 里的 key,另外一组值用于保存 Map 里的 value,key 和 value 可以是任意引用类型的数据。key 值不允许重复,可以为 null。如果添加 key-value 对时 Map 中已经有重复的 key,则新添加的 value 会覆盖该 key 原来对应的 value。常用实现类有 HashMap、LinkedHashMap、TreeMap等。
Map 声明的方法:
点击查看方法摘要
HashMap
HashMap 基于 hashing 原理,通过 put() 和 get() 方法存储和获取对象。当我们将键值对传递给 put() 方法时,它调用键(key)对象的 hashCode() 方法来计算 hashCode 值,然后找到对应的 bucket 位置来储存值(value)对象。当获取对象时,通过键(key)对象的 equals() 方法找到正确的键值对,然后返回对象。HashMap使用链表来解决碰撞问题,当发生碰撞了,对象将会存储在链表的下一个节点中。
原理图 1:
注:JDK1.8中,HashMap采用数组+链表+红黑树实现,当链表长度超过阈值(8)时,将链表转换为红黑树,这样大大减少了查找时间。
原理图 2:
原理图 3:
如上“原理图 3”,散列表中有散列数组,散列数组存放的元素是线性表(散列桶)。往散列表存放数据是键值对放进去,“Mac”和“Jerry”的HashCode一样,所以计算出来的散列值一样,假设该散列值计算得到的下标是2,就将“Mac”和“Jerry”存放在散列数组下标为2的这个散列桶里。而“Andy”的散列值计算得到的下标是8,那么“Andy”就保存在下标为8的散列桶里。由于所存放的数据只在下标为2和8的散列桶中,所以散列数组中其它位置则为空值。要找25这个元素,只要计算“Jerry”的散列值,最后计算得到下标2,然后在对应的散列桶中依次查找“Jerry”,就可以找到对应的25。
其实真正意义上的散列表如下图所示,以上“原理图”只是说明原理罢了,使用HashMap时将其想象成多行两列的表格即可。
真正存取的有意义的元素是key值所对应的value值,key值只不过是value值所取的名字,为了方便以后的查找之用。
特点梳理:
-
底层采用哈希表+链表或红黑树实现。
-
哈希表初始容量 1<<4,最大容量 1<<30,默认负载因子0.75f。
-
阈值为负载因子*容量,到达阈值时扩容,扩容为两倍,或者在最开始的时候,这是为了如果预计Hashmap会很大,可以把初始值调大。
-
当一个桶的节点数大小到达 8,开始从链表转换为红黑树。当一个红黑树大小到达 6,退化为链表。
-
线程不安全
HashMap 常用方法
点击查看方法摘要
Hashtable
HashMap 与 Hashtable 是 Map 接口的两个典型实现,它们之间的关系完全类似于 ArrayList 与 Vertor。HashTable 是一个古老的 Map 实现类,它提供的方法比较繁琐,目前基本不用了。
原理图:
LinkedHashMap
对于 LinkedHashMap 而言,它继承自 HashMap,底层使用哈希表与双向链表来保存所有元素。其基本操作与父类HashMap相似。LinkedHashMap 使用双向链表来维护 key-value 对的次序(其实只需要考虑 key 的次序即可),该链表负责维护 Map 的迭代顺序,默认与插入顺序一致(默认情况,遍历时的顺序是按照插入节点的顺序),即在每次插入数据,或者访问、修改数据时,会增加节点、或调整链表的节点顺序,以决定迭代时输出的顺序。
可以在构造时传入 accessOrder 参数,使得其遍历顺序按照访问的顺序输出。
原理图:
特点梳理:
-
继承自HashMap
-
额外使用类似于LinkedList的双向链表,维护插入顺序
-
线程不安全,效率高
-
键和值都可以是 null
-
底层由双向链表和哈希表组成
-
键元素:唯一(由
哈希表结构
保证)、有序(按照录入顺序,由链表结构
保证)
LinkedHashMap 的常用方法
点击查看方法摘要
TreeMap
TreeMap 是 SortedMap 的实现类,是一个红黑树的数据结构,每个 key-value 对作为红黑树的一个节点。TreeMap 存储 key-value 对时,需要根据 key 对节点进行排序。
TreeMap 继承自 AbstractMap,实现了 Map, Cloneable, NavigableMap, Serializable 接口:
-
TreeMap 继承于 AbstractMap,而 AbstractMap 实现了 Map 接口,并实现了 Map 接口中定义的方法,减少了其子类继承的复杂度
-
TreeMap 实现了 Map 接口,成为 Map 框架中的一员,可以包含着 key-value 形式的元素
-
TreeMap 实现了 NavigableMap 接口,意味着拥有了更强的元素搜索能力
-
TreeMap 实现了 Cloneable 接口,实现了 clone() 方法,可以被克隆
-
TreeMap 实现了 Java.io.Serializable 接口,支持序列化操作,可通过 Hessian 协议进行传输
特点梳理:
-
底层采用红黑树实现
-
保存的元素按照一定的规则有序
-
可以传入比较器,即可自定义排序规则,不然就按照key排序
-
非线程安全
TreeMap 常用方法
点击查看方法摘要
TreeMap 排序方式
-
自然排序:TreeMap 的所有 key 必须实现 Comparable 接口,而且所有的 key 应该是同一个类的对象,否则会抛出ClassCastException。
-
定制排序:创建TreeMap时,传入一个Comparator对象,该对象负责对TreeMap中的所有key进行排序。
WeakHashMap
WeakHashMap 继承于AbstractMap,实现了Map接口。和HashMap一样,WeakHashMap 也是一个散列表,它存储的内容也是键值对(key-value)映射,而且键和值都可以是null。不过WeakHashMap的键是“弱键”。在 WeakHashMap 中,当某个键不再正常使用时,会被从WeakHashMap中被自动移除。更精确地说,对于一个给定的键,其映射的存在并不阻止垃圾回收器对该键的丢弃,这就使该键成为可终止的,被终止,然后被回收。某个键被终止时,它对应的键值对也就从映射中有效地移除了。
这个“弱键”的原理呢?大致上就是,通过WeakReference和ReferenceQueue实现的。 WeakHashMap的key是“弱键”,即是WeakReference类型的;ReferenceQueue是一个队列,它会保存被GC回收的“弱键”。实现步骤是:
-
新建WeakHashMap,将“键值对”添加到WeakHashMap中。
实际上,WeakHashMap是通过数组table保存Entry(键值对);每一个Entry实际上是一个单向链表,即Entry是键值对链表。 -
当某“弱键”不再被其它对象引用,并被GC回收时。在GC回收该“弱键”时,这个“弱键”也同时会被添加到ReferenceQueue(queue)队列中。
-
当下一次我们需要操作WeakHashMap时,会先同步table和queue。table中保存了全部的键值对,而queue中保存被GC回收的键值对;同步它们,就是删除table中被GC回收的键值对。
这就是“弱键”如何被自动从WeakHashMap中删除的步骤了。
WeakHashMap 常用方法
点击查看方法摘要
IdentityHashMap
IdentityHashMap 比较 key 时是“引用相等”,即对于 k1 和 k2,当k1 == k2 时,IdentityHashMap 认为两个key相等。
IdentityHashMap 允许使用 null 作为 key 和 value.,不保证任何 Key-value 对之间的顺序, 更不能保证他们的顺序随时间的推移不会发生变化。
IdentityHashMap 有其特殊用途,比如序列化、深度复制、记录对象代理等。
IdentityHashMap 常用方法
点击查看方法摘要
EnumMap
Map 接口的实现,其 key-value 映射中的 key 是 Enum 类型。其原理就是一个对象数组,数组的下标索引就是根据 Map 中的 key 直接获取,即枚举中的 ordinal 值。效率比 HashMap 高,可以直接获取数组下标索引并访问到元素。
EnumMap 常用方法
点击查看方法摘要
Properties
Properties 类是 Hashtable 类的子类,它相当于一个 key、value 都是 String 类型的 Map ,主要用于读取配置文件。
Properties 常用方法
点击查看方法摘要