一、集合概述
1、集合和数组都是多个数据进行存储操作的结构,简称Java容器。此时的存储主要指的是内存层面的存储,不涉及到持久化的存储
1.1、 数组的存储多个数据的特点:
> 数组定义并初始化后,其长度也就确定了,而且元素类型也确定了,我们就只能操作指定类型的数据了
1.2、 缺点:
> 数组长度确定后,就不能对其长度进行修改
> 数组中提供的方法非常有限,这就导致对数组中的添加、删除、插入等操作,非常不便,效率很低
> 数组存储的数据特点是:有序的、可重复,对于无序、不可重复的需求,不能满足
1.3、 因为数组的不足,所以引入了集合,Java集合可以分为Collection 和 Map 两种体系
> Collection 接口: 单列数据,存储一组对象的方法和集合
> list: 元素有序、可重复 (ArrayList 、 LinkedList 、 Vector)
> set : 元素无序、不可重复 (HashSet 、 LinkedHashSet 、 TreeSet)
> Map接口: 双列数据,保存具有映射关系的集合 (HashMap 、 LinkedHashMap 、 TreeMap 、 Hashtable 、 Properties)
二、 Collection通用方法
add(Object obj) : 添加
addAll(Collection coll) : 添加集合
int size() : 获取有效元素个数
clear() : 清空
boolean isEmpty() : 集合是否为空
boolean contains(Object obj) : 集合中是否包含某个元素
boolean containAll(Collection coll) : 集合中是否包含集合中的所有元素 contain和containAll 都是通过调用对象的所在类的equals方法来比较元素的
boolean remove(Object obj) : 移除某个元素
boolean removeAll(Collection coll) : 移除集合中的元素
retainAll(Collection coll) : 取两个集合的交集
boolean equals(Object obj) : 集合是否相等,当集合是ArrayList时,两个集合中的元素相同,但是顺序不一致也是false
Object toArray() : 转成对象数组 -----> 数组转换为集合 : Arrays工具类中的 asList();
hashCode() : 获取集合对象的hash值
iterator() : 迭代器
三、迭代器
为容器而生,在不暴露对象的内部细节,访问容器中的元素
1、 方法:
> hasNext(): 判断集合是否还有下一个元素
> next(): 指针下移,将下移后的集合位置上的元素返回
> remove() : 删除指针所指集合位置上的元素
注意: 每次调用iterator() 方法都会返回一个新的迭代器对象,每个迭代器对象的初始指针都在集合首元素的前一个位置,另外,在迭代器刚刚创建的时候或还没有调用next方法时不能调用remove方法
2、foreach循环遍历集合元素 jdk5.0 新增的方式 foreach底层调用的也是迭代器,注意的时普通的for循环和 foreach 增强for循环的不同点,增强循环将读到的元素赋值给一个变量,修改该变量不会影响原元素
四、List接口
1、 List接口 可以认为是“动态” 数组, 它的存储是有序的,可重复的数据,主要的实现类有如下
> ArrayList: 是List接口的主要实现类,线程不安全的,效率高, 底层使用Object[] 存储
> LinkedList: 底层使用的是双向链表的结构进行存储,主要使用在要对集合频繁插入、删除操作的情况
> Vector: 1.0版本出现,是古来的实现类,且是线程安全的,效率低
2、 ArrayList源码分析: jdk7.0 版本下,ArrayList底层是先创建了一个长度为10的Object[] ,当我们添加数据的时候会先判断数组的长度是否足够,不足则会扩容, 扩大为原来的1.5倍,并将原来数组中的元素复制到新的数组中
ArrayList中含有一个带参的构造器,可以创建指定容量的Objec[] 数组
Jdk8.0版本下,ArrayList底层不会再创建集合的时候创建一个Object[] 数组, 而是在首次添加数据的时候,创建一个长度为10的数组,并将数据添加到集合的第一个位置上,后续的添加和扩容操作和jdk1.0一样
jdk7.0 的创建方式类似于单例的饿汉式,而jdk8.0中的ArrayList的对象创建类时于单例的懒汉式
3、常用方法:
增:add(Object obj)
删:remove(Object obj) or remove(int index)
改:set(int index , Object ele)
查:get(int index)
插:add(int index, object obj)
长度:size()
五、set接口
1、set接口存储的是无序的,不可重复的,类时于数学中的“ 集合 ”, 主要的实现类如下
> HashSet:作为set接口的主要实现类,线程不安全的,可以存储null值
> LinkedHashSet: 作为HashSet的子类,遍历其内部数据时,可以按照添加的顺序遍历
>TreeSet:可以按照添加对象的指定属性,进行排序
2、理解set的无序性和不可重复性:这里的无序性不等于随机性,存储的数据在底层数组并非按照数组索引的顺序添加,而是根据数据的哈希值
不可重复性就是指集合中的元素不能重复,按照equals() 判断,不能返回true,即相同的元素只能添加一个
添加元素的过程,以HashSet为例:
假设我么向HashSet中添加元素 a ,首先调用 元素a 所在类的hashCode() ,计算出元素a 在底层数组中存放的位置,如果元素a 要存放的位置没有元素,则添加成功
如果元素a 要存放的位置已经存在元素b ,比较元素a 和元素b 的哈希值,如果两个元素的哈希值不同,则元素a 添加成功
如果两个元素的哈希值相同,则调用元素a 所在的equals() ,如果equals() 返回true , 则元素a 添加失败 ,反之, 则添加成功
LinkedHashSet: 在HashSet的基础上给每个元素添加了前驱节点和后继节点,让频繁插入、删除等操作更加的高效
六、 Map
1、Map: 双列数据,存储 key - value 对的数据,类似于函数
> HashMap: 作为Map的主要实现类 , 线程不安全,效率高, 可以存储null的 key 和value
> linkedHashMap: 遍历集合的时候可以按照添加顺序实现遍历,因为在原有的HashMap底层结构上,添加了一对指针,指向前一个和后一个 元素
> TreeMap : 保证按照添加的key - value 对进行排序,实现排序遍历,此时考虑key的自然排序或定制排序
> Hashtable : 作为古老的实现类 , 线程安全的 ,效率低,不可以存储null的 key 和 value
> properties : 常用来处理配置文件, key 和value 都是String类型
2、 Map的结构理解
Map中的key 是无序的、不可重复的,使用set存储 所有的key ----> 故key所在的类要重写equals() 和 hashCode()
Map中的value是无序的,可重复的,使用Collection存储所有的value ----> 故value所在的类要重写equals()
一个键值对key - value 就代表着一个entry,entry是无序的,不可重复的,使用set 来存储所有的entry
3、HashMap底层: 数组 + 链表 (jdk 7.0及之前)
数组 + 链表 + 红黑树 (jdk 8)
底层实现原理:
> JDK 7.0, HashMap map = new HashMap(); 实例化后,底层会创建一个长度为16的Entry[] 数组,当我们添加数据(key1 - value1)时, 首先调用key1所在类的hashCode() 方法,通过某种算法得到该键值对在 Entry数组中的位置
如果该位置上没有元素则添加数据(key1 - value1)成功
如果该位置上有元素,也就是说该位置上已经有一个或多个数据(以链表的形式存在),比较key1 和 这些元素的哈希值
如果哈希值不相同,则添加数据(key1 - value1)成功
如果哈希值相同,则继续比较,调用key1所在类的equals() 方法
如果equals() 返回false,则添加数据(key1 - value1)成功,返回true则将用value1替换原来的value值
扩容问题:当数组的长度大于临界值才会扩容(为什么要提前扩容? --- > 尽可能使链表的数量少), 默认的扩容方式是扩容为原来的2倍,并将原来的数据复制过来
> JDK 8,于7不同的是,在实例化HashMap的时候,底层没有创建一个长度为16的数组,而是在调用put() 方法的时候才创建长度为16的数组,该数组不再是Entry[] 而是 Node[]
7底层的结构是 : 数组 + 链表, 8底层的结构是: 数组 + 链表 + 红黑树( 当数组的某个索引位置上的元素以链表的形式存在的数据个数 > 8,且当前数组的长度 > 64时,此时索引位置上的所有数据改为使用红黑树存储 )
改为红黑树的原因: 便于遍历,提高查找的效率
HashMap源码的重要常量:
DEFAULT_INITIAL_CAPACITY : HashMap的默认容量,16
DEFAULT_LOAD_FACTOR:HashMap的默认加载因子 0.75
threshold:扩容的临界值,=容量*填充因子 12
TREEIFY_THRESHOLD:Bucket中链表长度大于该默认值,转化为红黑树
MIN_TREEIFY_CAPACITY:桶中的Node被树化时最小的hash表容量 64
4、LinkedHashMap:
对HashMap中的node进行类继承,并添加了before 和after ,进而直到前一个元素和下一个元素的位置, 有利于对 插入 、 删除等的操作
5、 常用方法
> 增 : put(Object key , Object value)
> 删 : remove(Object key)
> 查 : get(Object key)
> 改 : put(Object key, Object value)
> 遍历 :keySet() 、 values() 、 entrySet()
6、TreeMap:
TreeMap中添加key - value,要求key来自同一个类,该类还要提供 自然排序 或 定制排序,因为要按照key进行排序