Java 的集合类很多, 主要分为两大类
单列集合Collection,双列集合Map
- Collection实现子类可以存放多个元素,每个元素可以是Object
- 有些Collection的实现类,可以存放重复的元素,有些不可以
- 有些Collection的实现类,有些是有序的(List),有些不是有序(Set)
- Collection接口没有直接的实现子类,是通过它的子接口Set和List来
实现的
- Iterator对象称为迭代器,主要用于遍历Collection集合中的元素。
- 所有实现了Collection接口的集合类都有一个iterator(方法, 用以返回
一个实现 了Iterator接口的对象,即可以返回一个迭代器。 - Iterator的结构.[看一 张图]
- Iterator仅用于遍历集合,Iterator本身并不存放对象。
List接口是Collection接口的子接口List java
- List集合类中元素有序(即添加顺序和取出顺序一致)、 且可重复。
- List集合中的每个元素都有其对应的顺序索引,即支持索引。
- List容器中的元素都对应一个整数型的序号记载其在容器中的位置,可以根据序号存取容器中的元素。
- JDK API中List接口的实现类有:
- permits all elements, including null , ArrayList可以加入null,并且多个
- ArrayList是由数组来实现数据存储的
- ArrayList基本等同于Vector,除了ArrayList是线程不安全(执行效率高)
在多线程情况下,不建议使用ArrayList
- ArrayList中维护了一个0bject类型的数组elementData. [debug看源码]
transient Object[] elementData; //transient表示瞬间,短暂的,表示该属性不会被序列化 - 当创建ArrayList对象时,如果使用的是无参构造器,则初始elementData容量为0,第1次添加,则扩容elementData为10,如需要再次扩容,则扩容elementData为1.5倍。
- 如果使用的是指定大小的构造器,则初始elementData容量为指定大小,如果需要扩容,则直接扩容elementData为1.5倍。
- Vector底层也是一个对象数组,protected Object[] elementData;
- Vector 是线程同步的,即线程安全,Vector类的操作方法带有synchronized
public synchronized E get(int index) {
if (index > = elementCount)
throw new ArrayIndexOutOfBoundsException(index);
return elementData(index);
}
- 在开发中,需要线程同步安全时,考虑使用Vector
Vector 和 ArrayList 的比较
- linkedList底层实现了双向链表和双端队列特点
- 可以添加任意元素(元素可以重复),包括null
- 线程不安全,没有实现同步
- linkedList底层维护了一个双向链表.
- linkedList中维护了两个属性first和last分别指向首节点和尾节点
- 每个节点(Node对象),里面又维护了prev、next、item三个属性, 其中通过prev指向前一个,通过next指向后一一个节点。最终实现双向链表.
- 所以linkedList的元素的添加和删除,不是通过数组完成的,相对来说效率较高。
ArrayList 和 linkedList 的比较
如何选择ArrayList和linkedList:
- 如果我们改查的操作多,选择ArrayList。
- 如果我们增删的操作多,选择linkedList。
- 一般来说,在程序中,80%-90%都是查询,因此大部分情况下会选择ArrayList。
- 在一个项目中,根据业务灵活选择,也可能这样,一个模块使用的是ArrayList,另外一个模块是linkedList,也就是说,要根据业务来进行选择。
Set 接口基本介绍
- 无序(添加和取出的顺序不一致) ,没有索引
- 不允许重复元素,所以最多包含一个null
同Collection的遍历方式-样,因为Set接口是Collection接口的子接口。
- 可以使用迭代器
- 增强for
- 不能使用索引的方式来获取.
HashSet 的全面说明
- HashSet实现了Set接口
- HashSet实际上是HashMap,看下源码.
public HashSet() {
map = new HashMap<>();
}
- 可以存放null值,但是只能有一个null
- HashSet不保证元素是有序的,取决于hash后,再确定索引的结果. (即,不
保证存放元素的顺序和取出顺序一 致) - 不能有重复元素/对象.在前面Set接口使用已经讲过
HashMap底层是(数组+链表+红黑树)
- HashSet 底层是HashMap
- 添加一个元素时,先得到hash值-会转成->索引值
- 找到存储数据表table ,看这个索引位置是否已经存放的有元素
- 如果没有,直接加入
- 如果有,调用equals比较,如果相同,就放弃添加,如果不相同,则
添加到最后 - 在Java8中,如果一条链表的元素个数到达TREEIFY THRESHOLD(默认是8),并且table的大小 >=MIN TREEIFY CAPACITY(默认64),就会进行树化(红黑树),否则仍然采用数组扩容机制。
HashSet看源码!
HashSet源码解读
- HashSet底层是HashMap,第一次添加时,table 数组扩容到16,临界值(threshold)是16*加载因子(loadFactor)是0.75 = 12
- 如果table数组使用到了临界值12,就会扩容到16* 2 = 32,新的临界值就是32*0.75 = 24,依次类推
- 在Java8中, 如果一条链表的元素个数到达TREEIFY THRESHOLD(默认是8 ),并且table的大小>=MIN TREEIFY CAPACITY(默认64),就会进行树化(红黑树),否则仍然采用数组扩容机制
linkedHashSet
- linkedHashSet是HashSet的子类
- linkedHashSet底层是一个linkedHashMap,底层维护了一个数组+双
向链表 - linkedHashSet根据元素的hashCode值来决定元素的存储位置,同时使
用链表维护元素的次序(图),这使得元素看起来是以插入顺序保存的。 - linkedHashSet不允许添重复元素
- 在linkedHastSet 中维护了一个hash表和双向链表( linkedHashSet有head和tail )
- 每一个节点有before和after属性,这样可以形成双向链表
- 在添加一个元素时,先求hash值,在求索引.确定该元素在table的位置,然后将添加的元素加入到双向链表(如果已经存在,不添加[原则和hashset一样])
- 这样的话,我们遍历linkedHashSet也能确保插入顺序和遍历顺序一致
Map
- Map与Collection并列存在。 用于保存具有映射关系的数据:Key-Value
- Map中的key和value可以是任何引用类型的数据,会封装HashMap$Node对象中
- Map中的key不允许重复,原因和HashSet一样,前面分析过源码.
- Map中的value可以重复
- Map的key可以为null, value也可以为null,注意key为null,只能有一个,value为null可以多个。
- 常用String类作为Map的key
- key和value之间存在单向一对一关系,即通过指定的key总能找到对应的value
- Map存放数据的key-value示意图,-对k-v是放在一-个HashMap$Node中的,因为Node实现了Entry 接口,有些书上也说一对k-v就是一个Entry
entrySet
HashMap 小结
- Map接口的常用实现类: HashMap、Hashtable和Properties
- HashMap是Map接口使用频率最高的实现类。
- HashMap是以key-val对的方式来存储数据(HashMap$Node类型)
- key不能重复,但是值可以重复,允许使用null键和null值。
- 如果添加相同的key,则会覆盖原来的key-val等同于修改.(key不会替换,val会替换)
- 与HashSet-样,不保证映射的顺序,因为底层是以hash表的方式来存储的. (jdk8的hashMap底层数组+链表+红黑树)
- HashMap没有实现同步,因此是线程不安全的,方法没有做同步互斥的操作,没有synchronized
- (k,v)是一个Node实现了Map.Entry
,查看HashMap的源码可以看到. - jdk7.0的hashmap底层实现[数组+链表],jdk8.0底层[数组+链表+红黑树]
➢扩容机制[和HashSet相同]
- HashMap底层维护了Node类型的数组table,默认为null
- 当创建对象时,将加载因子(loadfactor)初始化为0.75。
- 当添加key-val时,通过key的哈希值得到在table的索引。然后判断该索引处是否有元素,如果没有元素直接添加。如果该索引处有元素,继续判断该元素的key和准备加入的key相是否等,如果相等,则直接替换val;如果不相等需要判断是树结构还是链表结构,做出相应处理。如果添加时发现容量不够,则需要扩容。
- 第1次添加,则需要扩容table容量为16,临界值(threshold)为12 (16*0.75)
- 以后再扩容,则需要扩容table容量为原来的2倍(32),临界值为原来的2倍,即24,依次类推。
- 在Java8中,如果一条链表的元素个数超过 TREEIFY THRESHOLD(默认是8),并且table的大小>= MIN TREEIFY CAPACITY(默认64),就会进行树化(红黑树)
HashTable
- 存放的元素是键值对:即K-V
- hashtable的键和值都不能为null, 否则会抛出NullPointerException
- hashTable使用方法基本上和HashMap-样
- hashTable是线程安全的(synchronized), hashMap是线程不安全的
到8个进行扩容。(8=11*0.75)
扩容之后11*2+1=23
Map接口实现类——
Properties
- Properties类继承自Hashtable类并且实现了Map接口,也是使用一种键值对的形式来保存数据。
- 它的使用特点和Hashtable类似。
- Properties还可以用于从xxx.properties文件中,加载数据到Properties类对象,并进行读取和修改



