Java集合----思维导图
下载地址: https://download.csdn.net/download/q863672107/20016476
集合内容比较多
以下是自动转换的MakeDown文本
集合
集合框架
单列集合
-
Collection接口
-
List接口
-
实现类
-
ArrayList类
主要实现类-
数组实现的
- 查询快, 增删慢
-
-
LinkedList类
-
双向链表实现的
- 增删快, 查询慢
-
-
Vector类
古老实现类1.0-
同ArrayList
- 线程安全的
效率低
- 线程安全的
-
-
-
特点
-
有索引
-
可以存储重复元素
- 存取有序
-
-
-
-
Set接口
-
特点
-
无索引
-
不可存储重复元素
- 存取无序
-
-
-
实现类
-
HashSet类
-
底层是HashMap
- 哈希表(数组+链表+红黑树(JDK8))
-
-
LinkedHashSet类
-
底层LinkedHashMap
-
哈希表(数组+链表/红黑树(JDK8))+双向指针
-
不同于其他Set集合, LinkedHashMap底层在哈希表基础上, 每个节点还保存了对添加的前一个和后一个节点的引用, 保证存取顺序
- 遍历效率高
-
-
-
TreeSet类
-
底层TreeMap
-
红黑树
-
一般用于排序
不能为null- 存储的元素必须为同一个类型
- 存储的key元素是Comparable接口实现类对象
- 存储的元素按compareTo()/compare()方法比较, 不能重复
- 或者在new TreeSet(Comparator c)时传入一个Comparator接口的对象(重写compare方法)
-
-
-
-
线程安全
- Collections.synchronizedSet(new HashSet<>())
- 使用JUC包里面的CopyOnWriteArraySet
-
-
-
双列集合
-
Map接口
-
特点
-
key-value对
- key值不可重复
-
-
HashMap类
- 哈希表(数组+链表+红黑树(JDK8))
-
LinkesHashMap类
- 哈希表+双向指针(保证迭代顺序)
存取有序, 遍历效率高
- 哈希表+双向指针(保证迭代顺序)
-
TreeMap类
-
红黑树
-
可根据key值排序
key值不能为null- 存储的key值必须为同一个类型
- 存储的key元素是Comparable接口实现类对象
- 存储的key按compareTo()/compare()方法比较, 不能重复
- 或者在new TreeMap(Comparator c)时传入一个Comparator接口的对象(重写compare方法)
-
-
-
Hashtable类
古老实现类1.0-
同HashMap
- 线程安全
效率低
- 线程安全
-
key跟value不能存储null值
-
-
Collection接口
方法
-
添加
- add(Object obj)
- addAll(Collection c)
-
获取有效元素个数
- int size()
-
清空集合
- void clear()
-
是否为空
- boolean isEmpty()
-
是否包含
-
boolean contains(Object obj)
- 调用元素的equals方法判断是否为同一个对象
-
boolean containsAll(Collection c)
- 也是调用元素的equals方法比较, 拿两个集合的元素挨个比较
-
-
删除
-
boolean remove(Object obj)
- 调用元素的equals方法找到要删除的第一个元素
-
boolean removeAll(Collection c)
- 取当前集合与c的差集, 修改当前集合
-
-
取两个集合交集
-
boolaen retainAll(Collection c)
- 修改当前集合, 不影响c
-
-
是否相等
-
boolean equals(Object obj)
- 对List集合, 元素相同, 顺序一致 返回true
- 对Set集合, 元素相同, 顺序不一致, 返回true
-
-
转成对象数组
- Object[] toArray()
-
获取哈希值
- hashCode()
-
遍历
-
iterator()
- 返回迭代器对象
-
Collections工具类
常用方法
-
添加多个元素
- static boolean addAll(Collection c, T… elements)
-
通过多个元素创建ArrayList
- static List asList(T… a)
-
极值(max为例)
- static T max(Collection coll)
- static Object max(Collection coll, Comparator c)
-
统计
- int frequency(Collection coll, Object obj)
-
批量替换
- static boolean replaceAll(List list, T oldVal, T newVal)
-
拷贝数组
-
static void copy(List dest, List src)
-
如果dest.size()<src.size(), 会报异常
还是少用吧List dest = new ArrayList(src.size());
Collections.copy(dest, src);//会报异常, 想想为什么List dest = Arrays.asList(new Object[src.size()]);
Collections.copy(dest, src);//不会报异常
-
-
-
排序
-
static void sort(List list)
- 自然排序
-
static void sort(List list, Comparator c)
- 定制排序
-
-
随机洗牌
- static void shuffle(List list)
-
反转
- static void reverse(List list)
-
交换元素
- static void swap(Object[] x, int a, int b)
同步控制
Collections常用方法: 同步控制
Collections类中提供了多个synchronizedXxx()方法, 可将执行集合包装成线程同步的集合, 从而将解决多线程并发访问集合时的线程安全问题
如将ArrayList , HashMap转换为线程安全的
- static Collection synchronizedCollection(Collection c)
- static List synchronizedList(List list)
- static <K,V> Map<K,V> synchronizedMap(Map<K,V> m)
- static Set synchronizedSet(Set s)
- static <K,V> SortedMap<K,V> synchronizedSortedMap(SortedMap<K,V> m)
- static SortedSet synchronizedSortedSet(SortedSet s)
遍历集合
Iterator接口
用于Collection集合
Iterator对象称为迭代器(设计模式的一种), 主要用于遍历集合中的元素。
GOF给迭代器模式的定义为: 提供一种方法访问一种容器(container)对象中各个元素, 而又不需暴露该对象的内部细节。迭代器模式, 就是为容器而生。
Collection接口继承了Iteraor接口, 该接口有一个iterator()方法, 用以返回一个实现了Iterator接口的对象
Iterator仅用于遍历集合, Iterator本身并不提供承装对象的能力, 如果需要创建Iterator对象, 则必须有一个被迭代的集合。
-
通过Collecction接口的iterator()方法返回Iterator对象, 迭代器不作为容器
集合对象每次调用iterator()方法都得到一个全新的迭代器对象, 默认游标都在集合第一个元素之前。
- 每次调用返回的是新的迭代器对象
-
方法
-
boolean hasNext()
-
判断是否还有下一个元素
- 指针到达集合末尾, 则返回false
-
-
E next()
-
①指针后移 ②将移动后指针位置上的元素返回
- 指针初始化位置在第一个元素前
-
-
default void remove()
-
迭代器可以在遍历过程中删除集合元素,
注意: 这是迭代器的方法, 不同于集合的remove()如果还未调用next()方法, 或在上一次调用next()之后已经调用过一次remove(), 再重复调用, 会报IllegalStateException
-
-
-
代码
Iterator iterator = list.iterator();
while (iterator.hasNext()){
System.out.println(iterator.next());
}
foreach(JDK5)(推荐)
用于Collection集合/数组
JDK5提供了foreach循环迭代访问Collection和数组
遍历操作不需要获取Collection或数组的长度, 无需使用索引访问元素。
遍历集合的底层调用Iterator完成操作
foreach还可以用来遍历数组
-
for(Object obj : coll){ }
- 底层仍然调用了迭代器
Consumor接口
Collection/Map/数组都可使用
-
coll.foreach(new Consumer() {@Override
public void accept(Object o) { }}); -
coll.foreach((obj)->{ });
- lambda表达式
-
coll.foreach(System.out::println)
Stream
Stream 中文称为 “流”,通过将集合转换为这么一种叫做 “流” 的元素序列,通过声明性方式,能够对集合中的每个元素进行一系列并行或串行的流水线操作。
换句话说,你只需要告诉流你的要求,流便会在背后自行根据要求对元素进行处理, 并返回结果。
数据源–>流–>中间操作 * n -->中间操作–>终止操作
实例化
-
串行流
- Collection#default Stream stream()
-
并行流
-
Collection#default Stream parallelStream()
- 性能高, 不能保证执行顺序
-
中间操作
凡是返回值为Stream的都是中间操作
中间操作是惰性操作, 只有遇碰到终止操作才会执行
-
无状态(stateless)
线程安全该操作的数据不受上一步操作的影响
-
过滤
-
Stream filter(Predicate<? super T> predicate)
-
代码
Stream stream = list.stream();
Stream integerStream = stream.filter(new Predicate() {
@Override
public boolean test(Integer i) {
return i > 10;
//返回值true表示留下的数据, false表示被过滤
}
});
-
-
-
映射
-
Stream map(Function<? super T, ? extends R> mapper)
-
代码
/**
- 映射
*/
Stream integerStream = stream.map((item) -> {
//return item * 2;
return item.hashCode();
//映射为元素的hashCode()的流
});
- 映射
-
-
-
查看
-
Stream peek(Consumer<? super T> action)
-
代码
/**
- 查看
*/
Stream peek = stream.peek((item) -> {
System.out.println(item);
});
- 查看
-
-
-
其他
-
map()
- mapToInt()
- mapTolong()
- mapToDouble()
-
flatMap()
- flatMapToInt()
- flatMapToLong()
- flatMapToDouble()
-
unordered()
-
-
-
有状态(statful)
非线程安全必须等上一步操作拿完全部元素后才可操作
-
限制流中个数
-
Stream limit(long maxSize)
-
代码
/**
- 限制数量
*/
Stream limit = stream.limit(10);
- 限制数量
-
-
-
排序
-
Stream sorted()
-
Stream sorted(Comparator<? super T> comparator)
-
代码
/**
* 排序
*/
Stream sorted = stream.sorted(new Comparator() {
@Override
public int compare(Integer o1, Integer o2) {
return o1.compareTo(o2);
}
});
-
-
其他
- distinct()
-
skip()
-
终止操作
触发数据的流动, 并收集结果, 最后终止流
只要返回值不是Stream就是终止操作
-
非短路操作
(non-short-circuiting)会遍历所有元素
-
合并
-
T reduce(T identity, BinaryOperator accumulator)
-
代码
/**
-
合并
*/
Optional reduce = stream.reduce((item1, item2) -> {return item1 + item2;
});
System.out.println(reduce.get());
-
-
-
-
收集
-
collect(Collector collector)
-
生成集合
List collect(Collectors.toList())/**
- 收集–>收集到List集合
*/
List list1 = stream.collect(Collectors.toList());
- 收集–>收集到List集合
-
-
-
计数
-
long count()
-
代码
/**
- 计数
*/
long count = stream.count();
- 计数
-
-
-
其他
-
forEach()
- forEachOrdered()
-
toArray()
-
max()
-
min()
-
-
-
短路操作
(short-circuiting)会在适当的时刻终止遍历, 类似break
- anyMatch()
- allMatch()
- nonMatch()
- findFirst()
- findAny()
方法链-流式编程
-
代码
List list1 = list.stream.filter(i -> i > 10)
.map(i -> i.hashCode()).sorted((i1, i2) -> i2-i1).limit(10)
.peek(System.out::println).collect(Collectors.toList());
注意点
- 流是一次性的, 被操作一次
(中间操作 / 终止操作)后就被关闭了 - 终止操作只能出现一次
List接口
实现类
-
ArrayList
-
源码分析
-
JDK7
-
空参构造 初始化长度为 10 的 Object[ ] 数组
-
add
-
检验容量
-
容量不足, 扩容
-
1.5倍扩容
- 仍然不足, 直接取需要的容量
-
最大容量Integer.MAX_VALUE
- OutOfMemoryError()
-
-
-
-
JDK8
-
空参构造 Object[ ] 初始化为{ }, 并没有创建数组, 第一次调用add()时创建长度为10的数组
- 类似单例懒汉式, 延迟数组的创建时间, 节省内存
-
-
-
-
LinkedList
-
源码分析
-
JDK7
-
两个Node 属性,first/last, 分别记录首尾节点
-
add
-
记录新节点为last节点
-
添加前last是否为null
-
是–>(首次添加)
- 记录新节点为first节点
-
-
-
remove
-
遍历找到索引节点
- 修改前后节点的prev, next指针
-
-
-
JDK8
-
add
-
判断是链尾还是中间插入元素
-
添加到链尾
- 同上
-
中间插入元素
-
遍历找到索引节点
- 添加节点
-
-
-
-
-
-
-
Vector
-
-
源码分析
- 初始化容量10
- 2倍扩容
-
-
-
Vector有一个子类Stack(了解)
- 栈也是通过数组实现的, 限制元素只能从数组尾部添加和删除–>先进后出, 后进先出
-
-
List接口的方法
-
void add(int index, Object ele)
- 在index位置插入ele元素
-
boolean addAll(int index, Collection eles)
- 从index开始将ele中的所有元素添加进来
-
Object get(int index)
- 获取指定index位置的元素
-
int indexOf(Object obj)
- 返回obj在集合中首次出现的索引
-
int lastIndexOf(Object obj)
- 返回obj在集合中末次出现的索引
-
Object remove(int index)
- 移除指定index位置的元素, 并返回原有元素
-
Object set(int index, Object ele)
- 替换指定index位置的元素为, 并返回原有元素
-
List sublist(int fromIndex, int toIndex)
- 返回从fromIndex到toIndex的子集合, 左闭右开
常用方法小结
-
增
- add(Object obj)
-
删
- remove(int index)/remove(Object obj)
-
改
- set(int index, Object ele)
-
查
- get(int index)
-
插
- add(int index)
-
长度
- size()
-
遍历
- foreach()
Set接口
实现类
-
HashSet
-
-
源码分析
见HashMap-
JDK7
-
初始化数组长度16
-
扩容机制
- 负载因子, 默认0.75
-
-
哈希冲突时, 将新元素存储到链表头部(数组中)
-
-
JDK8
- 哈希冲突时, 将新元素添加到链表尾部
- 链表长度达到8, 且集合容量超过64时, 将链表转换为红黑树
-
-
-
-
LinkedHashSet
-
(有序性)
- 作为HashSet的子类, 遍历其内部元素时,
可以按照添加时的顺序遍历 - 其存储数据的结构依然与HashSet相同,
只是在添加数据的同时, 每个节点还维护了两个引用, 分别指向其添加的前一个和后一个元素 - 优点: 对于频繁的遍历操作, LinkedHashSet性能优于HashSet
- 作为HashSet的子类, 遍历其内部元素时,
-
-
TreeSet
-
排序
-
自然排序
- 集合元素实现Compareable接口
- 自然排序中, 比较两个对象是否相同的标准为: compareTo()方法返回0, 不再是equals()
-
定制排序
- 构造集合时传入实现Comaprator接口的比较器
- 定制排序中, 比较两个对象是否相同的标准为: compare()方法返回0, 不再是equals()
-
-
无序性
-
不等于随机性
- 存储的数据并非按照数据添加顺序存储, 而是根据元素的哈希值决定的。
不可重复性
-
先用hash算法判断(数组索引位置),
再用hashCode, ==和equals判断元素是否相等-
存储元素一定要根据对象的属性
重写hashCode()和equals()方法
两个方法尽可能保持一致性- 以实现对象相等原则
相等的对象必须具有相等的散列码
- 以实现对象相等原则
-
如果不重写hashCode方法, 调用Object的hashCode方法, 是根据对象地址值计算的,
地址不同, 则两个对象hashCode不同-
(了解)编译器自动重写hashCode用到乘以系数31
- 选择系数的时候要选择尽量大的系数, 计算出来的hash地址越大, 所谓的冲突就越少, 查找的效率就越高
2.hash值的大小又不宜造成数据溢出, 31只占用5bits, 相乘造成数据溢出的概率较小
-
31可以由 i*31==(i<<5)-i 来表示, 现在很多虚拟机里面都有做相关优化。(提高算法效率)
-
31是一个素数, 乘以素数可以减少hashCode冲突额概率, 还是较少哈希冲突
-
-
-
练习
将List内的重复数组去除
public static List duplicateList(List list){
HashSet set = new HashSet();
set.addAll(list);//将List集合中所有元素添加到Set集合(去重)
return new ArrayList(set);//通过Set集合创建List集合
}
Map接口
实现类
-
HashMap
主要实现类-
源码分析
-
区别
-
JDK7
-
数组+链表(哈希表)
-
实例化
-
实例化之后, 底层创建了默认初始化长度为16的数组: Entry<K,V>[ ] table
- 其实Map存的数据是一个个Entry<K,V>
-
-
添加元素
- JDK7中, 新增元素添加到链表头(table中)
-
-
JDK8
-
数组+链表+红黑树(哈希表)
- 当数组某一个索引位置上的链表长度>8 && 当前数组长度>64, 该索引位置上的元素改为使用红黑树存储
-
实例化
-
JDK8中, new HashMap()底层没有创建一个长度为16的数组
-
JDK8底层的数组是Node[], 而非Entry[]
-
JDK8中首次调用put()方法时, 创建默认长度为16的数组
-
自定义初始化长度时, 底层创建数组长度会确保为2的幂
这个算法在JDK8中也有优化
JDK7中:
int capacity = 1;
while(capacity < intitialCapacity)
capaciity <<= 1;JDK8中:
static final int tableSizeFor(int cap) {
int n = cap - 1;
n |= n >>> 1;
n |= n >>> 2;
n |= n >>> 4;
n |= n >>> 8;
n |= n >>> 16;
return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ?MAXIMUM_CAPACITY : n + 1;
}
原理: (对32位及以下)让为1的最高位后面位全部为1, 再+1即为2的幂。
cap-1 : 对于2的幂, 运算后结果变为其2倍, 如: 1000–>10000, 如果-1则能解决这个问题。- 确保数组长度为2的幂是为了方便在映射算法时通过位运算 & 计算, 处理hash值对数组长度取模
- x & (2^n -1) == x % 2^n
-
-
添加元素
- JDK8中, 新增元素, 添加到链表尾部
-
-
-
put(key1, value1)
-
调用key1所在类的hashCode()方法计算哈希值, 通过hash散列算法, 映射算法计算其在Node[ ] table中的存放索引
-
-
底层计算
-
hash算法
hash散列算法 :
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
当数组的长度很短时,只有低位数的hashCode值能参与运算。而让高16位参与运算可以更好的均匀散列, 减少hash冲突。在这里采用^运算而不采用& , | 运算, 原因在于^能更好的保留各部分的特征, &运算的结果会向1靠拢, | 运算的结果会向0靠拢
-
映射算法
对数组长度取模
-
-
-
判断桶(Bucket)是否为空
-
是
- 添加元素到桶中
-
否
-
比较key1和桶中已有的数据key的hash值
-
相等
-
通过==和equals判断是否相同
-
-
-
-
-
-
-
-