0902(045天 集合框架09 总结点 问)
每日一狗(田园犬西瓜瓜)
集合框架09 总结点 问
文章目录
1. 问:
补充:快速失败机制(快死异常)
多个线程对线程不安全的容器同时进行过操作时会抛出ConcurrentModificationException(并发修改异常)
原因:迭代器在遍历时直接访问集合中的内容,并且在遍历过程中使用一个 modCount变量。集合在被遍历期间如果内容发生变化,就会改变modCount的值。每当迭代器使用hashNext()/next()遍历下一个元素之前,都会检测modCount变量是否为expectedmodCount值,是的话就返回遍历;否则抛出异常,终止遍历。
解决办法:
- 在遍历过程中,所有涉及到改变modCount值得地方全部加上synchronized。
- 使用CopyOnWriteArrayList来替换ArrayList
- 或者你也可以接收异常,只有运行正确后才跳出循环
1.1 问:集合框架一些总结
常见的集合有哪些
Map接口和Collection接口是所有集合框架的父接口
-
Collection接口的子接口包括:Set接口和List接口
-
Map接口的实现类主要有:HashMap、TreeMap、Hashtable、ConcurrentHashMap以及Properties等(ConcurrentHashMap可以理解为HashMap的线程安全版)
-
Set接口的实现类主要有:HashSet、TreeSet、LinkedHashSet等
HashSet–HashMap TreeSet—TreeMap LinkedHashSet–LinkedHashMap
-
List接口的实现类主要有:ArrayList、LinkedList、Stack以及Vector等
ArrayList–CopyOnWriteArrayList
集合框架大总结
1、所有集合类都位于java.util包下。Java的集合类主要由两个接口派生而出:Collection和Map,
Collection和Map是Java集合框架的根接口,这两个接口又包含了一些子接口或实现类。
2、集合接口:6个接口(短虚线表示),表示不同集合类型,是集合框架的基础。
Collection List Set Queue Map Comparable Comparator Iterable Iterator
3、抽象类:5个抽象类(长虚线表示),对集合接口的部分实现。可扩展为自定义集合类。
AbstractList AbstractSet AbstractMap …
4、实现类:8个实现类(实线表示),对接口的具体实现。
ArrayList LinkedList HashSet TreeSet HashMap TreeMap Stack PriorityQueue
Vector LinkedHashSet Hashtable LinkedHashMap
5、Collection 接口是一组无序并允许重复的对象。
6、Set 接口继承 Collection,集合元素无序且不重复。
7、List接口继承 Collection,允许重复,维护元素插入顺序。
8、Map接口是键-值对象,与Collection接口没有什么关系。
9、Set、List和Map可以看做集合的三大类:
- List集合是有序集合,集合中的元素可以重复,访问集合中的元素可以根据元素的索引来访问。
- Set集合是无序集合,集合中的元素不可以重复,访问集合中的元素只能根据元素本身来访问(也是集合里元素不允许重复的原因)。
- Map集合中保存Key-value对形式的元素,访问时只能根据每项元素的key来访问其value。
10、遍历集合中的对象,Iterable iterator ListIterator[双向遍历]
特殊的迭代器(走访器)Enumeration属于已经不建议使用的方法
//@since 1.0
public interface Enumeration<E>
Enumeration<Integer> enu = list.elements();
while(enu.hasMoreElements()) {
Integer obj=enu.nextElement();
System.out.println(obj);
}
11、看Arrays和Collections。它们是操作数组、集合的两个工具类
Arrays:排序,折半查找,数组比较,数组深比较,数组展示
Collections:排序,把一些不安全的容器转换成安全的容器,批量元素替换
1.2 作业
-
ArrayList 和 Vector 的区别。
Vector线程安全 ,ArrayList 线程不安全。
ArrayList采用延迟初始化,延迟到第一次添加数据的时候
ArrayList默认扩容比为1.5,Vector有固定的扩容步长,没设定时扩容比为2.0
-
说说 ArrayList,Vector, LinkedList 的存储性能和特性。
ArrayList,Vector的存储都用数组
LinkedList采用双向链表
数组访问O(1),增删O(n)
双向链表访问O(n),增删O(1)
-
快速失败 (fail-fast) 和安全失败 (fail-safe) 的区别是什么?
快速失败:
安全失败:基于对底层集合进行拷贝
-
hashmap 的数据结构。
数组+链表(红黑树)
-
HashMap 的工作原理是什么?
HashMap
-
Hashmap 什么时候进行扩容呢?
当存储容量到达一定阈值时扩容(最大容积*负载因子)
当单链>8,总链<64时,也会进行扩容操作
-
List、Map、Set 三个接口,存取元素时,各有什么特点?
List:可以用对象比较,也可以用索引
Map:存取使用key值进行定位键值对
Set:存取使用对象比较方式进行
-
Set 里的元素是不能重复的,那么用什么方法来区分重复与否呢? 是用 == 还是 equals()? 它们有何区别?
hashCode和equals用的都是HashMap key的判定流程,TreeSet用的是TreeMap,这里用的红黑树,用的是比较器那一套
-
两个对象值相同 (x.equals(y) == true),但却可有不同的 hash code,这句话对不对?
可以,咱们自己封装的自定义数据类型,由于equals和hashCode方法写的不够全面才可能导致这种情况的发生。因为equals是判定两个对象是否相等用的,而hashCode是由另一个方法生成的,不知道咱们计算hashCode值所用属性和equals判定对象相同时所用的属性是否一致,所以这边一般不建议自己写这两个方法,直接用IDE进行生成。
hash code是以一个对象属性放到散列函数中计算出来的值,但这样想,两个对象的数据一样,但是放到散列函数中计算出来的值却不一样,这咋可能嘛,唯一的可能就是equals和hashCode中用的属性不一样,而且肯定是equals
-
heap 和 stack 有什么区别。
2. 流式编程
多线程针对集合的操作
Stream不是数据结构,不会存储数据,而是算法的封装。
操作是并行或并发执行的。
数据源本身是可以源源不断的
2.1 概念
定义
Stream API借助于同样新出现的Lambda表达式,极大的提高编程效率和程序可读性。
同时它提供串行和并行两种模式进行汇聚操作,并发模式能够充分利用多核处理器的优势,使用fork/join并行方式来拆分任务和加速处理过程。
通常编写并行代码很难而且容易出错, 但使用Stream API无需编写一行多线程的代码,就可以很方便地写出高性能的并发程序。
所以Java 8 中首次出现的 java.util.stream 是一个函数式语言+多核时代综合影响的产物。
元素
Stream不是集合元素,它不是数据结构并不保存数据,它是有关算法和计算的,它更像一个高级版本的Iterator。
原始版本的Iterator,用户只能显式地一个一个遍历元素并对其执行某些操作;高级版本的Stream,用户只要给出需要对其包含的元素执行什么操作,比如过滤掉长度大于10的字符串、获取每个字符串的首字母等,Stream会隐式地在内部进行遍历,做出相应的数据转换。
Stream就如同一个迭代器Iterator,单向,不可往复,数据只能遍历一次,遍历过一次后即用尽了,就好比流水从面前流过,一去不复返。
而和迭代器又不同的是,Stream可以并行化操作,迭代器只能命令式地、串行化操作。
顾名思义,当使用串行方式去遍历时,每个item读完后再读下一个item。而使用并行去遍历时,数据会被分成多个段,其中每一个都在不同的线程中处理,然后将结果一起输出。
Stream的并行操作依赖于Java7中引入的Fork/Join框架(JSR166y)来拆分任务和加速处理过程。
特点
数据源本身可以是无限的。
2.2 应用
当使用一个流的时候,通常包括三个基本步骤:
获取一个数据源source → 数据转换 → 执行操作获取想要的结果。
每次转换原有Stream对象不改变,返回一个新的Stream对象(可以有多次转换),这就允许对其操作可以像链条一样排列,变成一个管道。
Integer transactionsIds =
roomList.stream() // 将List转换为stream对象
.filter(b -> b.getLength() == 10) // 针对stream种的数据元素进行过滤,只使用为true
.sorted((x,y) -> x.getHigh() - y.getHigh()) // 针对stream种的数据进行自定义规则的排序
.mapToInt(Room::getWidth) // 从集合元素通过调用当前对象getWidth方法获取对应的int类型数据
.sum(); // 针对整数值进行求和处理
使用Stream步骤
- 创建Stream;
- 转换Stream,每次转换原有Stream对象不改变,返回一个新的Stream对象(可以有多次转换)
- 对Stream进行聚合(Reduce)操作,获取想要的结果;
创建Stream方法
创建方法1:可以使用Arrays.stream()方法来使用Stream
Integer[] array = new Integer[]{3,4,8,16,19,27,23,99,76,232,33,96};
long count = Arrays.stream(array)
.filter(i->i>20)
.count();
创建方法2:使用List创建一个并行流对象Stream
Stream<Integer> stream = list.parallelStream();
Stream<Integer> stream = list.stream();
创建方法3:Collection.stream()用Java集合都创建一个Stream
2.3 Stream的一些方法
-
collect 收集操作,将所有数据收集起来,这个操作非常重要,官方的提供的Collectors 提供了非常多收集器,可以说Stream 的核心在于Collectors
-
count 统计操作,统计最终的数据个数
-
findFirst、findAny 查找操作,查找第一个、查找任何一个 返回的类型为Optional
-
noneMatch、allMatch、anyMatch 匹配操作,数据流中是否存在符合条件的元素 返回值为bool 值
-
min、max 最值操作,需要自定义比较器,返回数据流中最大最小的值
-
reduce 规约操作,将整个数据流的值规约为一个值,count、min、max底层就是使用reduce
-
forEach、forEachOrdered 遍历操作,这里就是对最终的数据进行消费了
-
toArray 数组操作,将数据流的元素转换成数组。
-
map(mapToInt,mapToLong,mapToDouble) 转换操作符,把比如A->B,这里默认提供了转int,long,double的操作符
-
flatmap(flatmapToInt,flatmapToLong,flatmapToDouble) 拍平操作,比如把 int[]{2,3,4} 拍平变成 2,3,4 也
就是从原来的一个数据变成了3个数据,这里默认提供了拍平成int,long,double的操作符 -
limit 限流操作,比如数据流中有10个只要出前3个就可以使用
-
distint 去重操作,对重复元素去重,底层使用了equals方法
-
filter 过滤操作,把不想要的数据过滤
-
peek 挑出操作,如果想对数据进行某些操作,如:读取、编辑修改等
-
skip 跳过操作,跳过某些元素
-
sorted(unordered) 排序操作,对元素排序,前提是实现Comparable接口,当然也可以自定义比较器。
扩展小芝士
- 接口中只定义最基础的东西
- 对hashSet的元素修改时最好先取出来改完后再放回去,他会重新计算hashCode然后重新定位
- BigInteger的计算使用一个数组存着然后像手算一样来计算