概述
- 集合主要有两组,单列和双列
- Collection 接口主要分为 List Set, 他们的实现子类都是单列集合
- Map 接口实现子类是双列集合,存放的
<K,V>
Collection: Interface
Collection实现子类可以存放多个元素,每个元素都可以是 Object- 有些
Collection的实现类,可以存放重复的元素,有些不可以 - 有些
Collection的实现类,List是有序的 ,Set是无序的 Collection接口没有直接的实现子类,是通过它的子接口Set和List来实现的
| api | 说明 |
|---|---|
| add | 添加 |
| remove | 移除 |
| contains | 查找元素是否存在 |
| size | 获取元素个数 |
| isEmpty | 判断是否为空 |
| clear | 清空 |
| addAll | 添加多个 |
| containsAll | 判断是否存在多个 |
| removeAll | 添加多个 |
Collection接口继承了Iterable迭代器
public interface Collection <E> extends Iterable <E> {}
Iterable接口迭代器,主要用于遍历Collection集合中的元素。- 所有实现
Collection接口的集合类都有一个iterable()方法,用来返回一个实现了Iterable接口的对象,即可以返回一个迭代器 Iterable接口中的hasNext判断是否还有下一个元素next负责移动,超出则会抛出异常NoSuchElementException无此类元素
List: Interface
List集合中的元素是有序的(添加的顺序和取出的顺序一致,且可以重复),集合中的每个元素都有其对应的顺序索引,即支持索引(底层数一个数组),索引是从0开始的
继承了 Iterablen: Interface
List list = new ArrayList();
list.add("小文");
list.add("小明");
list.add("小军");
Iterator iterator = list.iterator();
// 循环方式一:使用 iterator
while (iterator.hasNext()){
Object next = iterator.next();
System.out.println(next);
}
// Object next = iterator.next(); // NoSuchElementException
// 重置游标
// list.iterator();
// 循环方式二:增强for循环,增强for也可以用作数组使用
for (Object next :list){
System.out.println(next);
}
// 循环方式三: 普通for循环
for (int i = 0; i < list.size(); i++) {
Object o = list.get(i);
System.out.println(o);
| api | 说明 |
|---|---|
| get | 获取索引 |
| indexOf | 返回第一个匹配到的索引 |
| lastIndexOf | 返回最后匹配到的索引 |
| set | 替换 |
| subList | 返回前闭后开的区间 |
ArrayList: Class
-
说明
ArrayList底层是由游数组实现,不建议在多线程中使用,线程不安全(效率高) -
扩容机制
ArrayList中维护了一个Object类型的数组transient Object[] elementData,transient关键之表示不会被序列化elementData无参构建,初始为0,第一次扩容为10,再次扩容为当前elementData的1.5倍- 如果初始指定
elementData大小,再次扩容为当前elementData的1.5倍
Vector: Class
-
说明
Vector线程安全,方法上都加了sybchronized(效率不高) -
扩容机制
Vector中维护了一个Object类型的数组protected Object[] elementData,transient关键之表示不会被序列化elementData无参构建初始为10,再次扩容为当前elementData的2倍- 如果初始指定
elementData大小,再次扩容为当前elementData的2倍
LinkedList: Class
- 说明
LinkedList底层实现双向链表和双端队列特点,线程不安全
可以看出在 LinkedList 中有 prev,item,next 三个属性 来表示链的关系
Set: Interface
- 说明
- 不允许重复元素,所以对多包含一个null
- 循环机制
- 迭代循环
- 增强for循环
- 没法使用 for循环,因为没有 .get(i)方法,因为是无序的
HashSet
- 说明
- 添加和取出的顺序不一致
- 底层是
new HashMap()实现, V 使用PRESENT常量填充,<K- PRESENT>,实现逻辑看后面 HashMap重点和扩容机制
LinkedHashSet
- 说明 \
- 这是一个双向链表结构
- 添加和取出的顺序一致
- 扩容机制和
HashSet一致
TreeSet
- 说明
- 当使用无参构造创建实例的时候,是无序的
new TreeSet(Comparator<T>),按照Comparator的逻辑排序- 底层是
TreeMap,底层是匿名内部类的comparator方法 - 如果有相同的值无法加入也就是
cmp = 0,查看TreeMap的排序机制
Map: Interface
- 说明
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
| api | 说明 |
|---|---|
| put | 添加 |
| remove | 根据键删除映射关系 |
| get | 根据key获取值 |
| size | 获取元素个数 |
| isEmpty | 判读是否为空 |
| clear | 清除 |
| containsKey | 查找键是否存在 |
- 循环方式
以 HashMap 为例
Map map = new HashMap();
for (int i = 0; i < 10; i++) {
Car car = new Car();
map.put("key"+i, car);
}
// ----------第一组 先取出所有的key,通过Key取出对应的 Value ----------
Set keySet = map.keySet();
// 1. 增强 for
System.out.println("1. 增强 for-------");
for (Object key : keySet) {
System.out.println(key + ":" +map.get(key));
}
// 2. 迭代器
System.out.println("2. 迭代器-------");
Iterator iterator = keySet.iterator();
while (iterator.hasNext()) {
Object key = iterator.next();
System.out.println(key + ":" +map.get(key));
}
// ---------- 第二组 把所有的 Value 取出 ----------
Collection values = map.values();
// 这里可以使用 Collection 使用的遍历方式
// 1. 增强 for
for (Object value : values) {
System.out.println(value);
}
// 2. 迭代器
Iterator iterator2 = keySet.iterator();
while (iterator2.hasNext()) {
Object value = iterator2.next();
System.out.println(value);
}
// ---------- 第三组 通过EntrySet 来获取 Key Value ----------
Set entrySet = map.entrySet();
// 1. 增强 for
for (Object o : entrySet) {
Map.Entry e = (Map.Entry) o;
System.out.println(e.getKey() +":"+ e.getValue());
}
System.out.println("第三组 2. 迭代器-------");
// 2. 迭代器
Iterator iterator3 = entrySet.iterator();
while (iterator3.hasNext()) {
// 向下转转型 Map.Entry
Map.Entry e = (Map.Entry) iterator3.next();
System.out.println(e.getKey() +":"+ e.getValue());
}
HashMap
- 说明
- 线程不安全,方法没有做同步互斥操作(
synchronized) - 不保证顺序
- 如果
put已经存在的key会覆盖
*重点
entrySet中存储的是HashMap$Node无序所有的实体类的引用值keySet中存储的是HashMap$Node中无序所有的key(Set 接口实现)values中存储的是HashMap$Node中无序所有的value(Collection 接口实现)- 添加一个元素时,先到hash值-随机转成一个索引
- 找到存储数据表table,看这个索引位置是否已经存放了有元素,如果没有直接加入
- 如果有,调用
equals比较,如果相同,则添加到最后 - 在Java 1.8中,如果一条链表的元素个数到达 TREEIFY_THRESHOLD(默认是8), 并且table大小 >= MIN_TREEIFY_CAPACITY(默认64),会进行树化(红黑树)
key会按照字节码的大小排序(但不严谨)
// HashMap Source Code
public class HashMap<K,V> extends AbstractMap<K,V>
implements Map<K,V>, Cloneable, Serializable{
transient Set<Map.Entry<K,V>> entrySet;
public Set<Map.Entry<K,V>> entrySet() {
/**
存储 entrySet 中存储 Node<K,V> implements Map.Entry<K,V>
*/
Set<Map.Entry<K,V>> es;
return (es = entrySet) == null ? (entrySet = new EntrySet()) : es;
}
static class Node<K,V> implements Map.Entry<K,V>{
// some code ...
}
final class EntrySet extends AbstractSet<Map.Entry<K,V>>{}
// some code ...
}
*扩容机制
- 初始扩容为 1 <<< 4 => 16
- 扩容临界值为0.75,如果到达临界值12就会扩容 16 * 2=32,新的临界值就是32*0.75,以此类推
重点:临界值扩容: 是统计的所有数值,不管是添加在链表还是在树化后的树中 hashCode决定了table的索引的位置(可以根据逻辑改写)equals决定了是否相等,如果相等则无法添加(可以根据逻辑改写)- 当在树化中的数据不断减少,减少到一定数量后,会重新变回链表(
这个过程叫减枝)
HashTable
- 说明
- 存放的是键值对
<K-V> - 线程安全,方法实现了
synchronized,效率不高 - 键值对都不能为
null,否则会抛出NullPointerException - 操作方式和
HashMap基本一致
*扩容机制
- 底层是数组
HashTable$Entry[]初始化大小为11 - 临界值
threshold 8 = 11* 0.75 - 执行方法
addEntry(); 添加 K-V 到 HashTable$Entry[] - 当
if (count >= threshold)满足时,就进行扩容(count 是HashTable$Entry[]的长度) - 按照
int newCapacity = (oldCapacity << 1) + 1的大小扩容
LinkedHashMap
- 说明
双向链表
TreeMap
- 说明 \
- 双向链表
- 如果
key, 相同无法加入
- 排序机制 \
- 可以通过
compare的机制cmp < 0,cmp > 0,cmp = 0,来修改排序机制
while (p != null) {
int cmp = cpr.compare(k, p.key);
if (cmp < 0)
p = p.left;
else if (cmp > 0)
p = p.right;
else
return p;
}
Properties
- 说明
- 键值对都不能为
null,否则会抛出NullPointerException
Collections
- 说明
集合的工具方法
| 常用 API | 说明 |
|---|---|
| sort | 不传Comparator的时候按照Ascll自然排序,传入 Comparator可以按照逻辑排序 |
| reverse | 反转顺序 |
| shuffle | 随机排序 |
| swap | 交换集合中指定元素的索引位置 |
| max | 根据元素自然顺序,返回给定集合中最大的元素, 可以传入 Comparator可以按照逻辑筛选 |
| min | 根据元素自然顺序,返回给定集合中最小的元素, 可以传入 Comparator可以按照逻辑筛选 |
| frequency | 集合中出现的次数 |
| copy(dest,list) | dest 的数据要 >= list |
| replaceAll | 替换集合中需要提替换的值 |
总结
- 先判断存储的类型是否是一组对象(单列)或一组键值对(双列)
- 一组对象(单列): Collection
- 允许重复 List:
- 增删多 -> LinkedList (底层维护了一个双向列表)
- 改查多 -> ArrayList (底层卫华 Object 类型的可以变数组)
- 不允许重复: Set:
- 无序 -> HashSet (底层是HashMap,卫华了一个哈希表,即 数组+链表+红树)
- 排序-> TreeSet 按照逻辑排序
- 插入和取出顺序需要一致 -> LinkedHashSet 维护了数组+双向链表
- 允许重复 List:
- 一组键值对(单列): Map
-
键无序-> HashMap(底层是:哈希表,数组+链表+红黑树)
-
键排序-> TreeMap 按照逻辑排序
-
键插入和取出顺序一致-> LinkedHashMap
-
读取文件-> Properties
-