集合类相关知识
apache 常用工具类的使用
参考:https://juejin.im/post/5b1695595188257d37761e68
SynchronizedList 和 Vector 的区别
相同点
- 都是线程安全的
- 底层都是使用数组来实现的
不同点(主要就是同步块和同步方法的区别) - SynchronizedList有很好的扩展和兼容功能。他可以将所有的List的子类转成线程安全的类。
- 使用SynchronizedList的时候,进行遍历时要手动进行同步处理。
- SynchronizedList可以指定锁定的对象。
Enumeration 和 Iterator 区别
//Enumeration 源码
public interface Enumeration<E> {
/**
* 是否存在元素
*/
boolean hasMoreElements();
/**
* 获取下一个元素
*/
E nextElement();
}
// Iterator源码
public interface Iterator<E> {
/**
* 是否存在下一个元素
*/
boolean hasNext();
/**
* 获取下一个元素
*/
E next();
/**
* 删除元素
*/
default void remove() {
throw new UnsupportedOperationException("remove");
}
/**
* Performs the given action for each remaining element until all elements
*/
default void forEachRemaining(Consumer<? super E> action) {
Objects.requireNonNull(action);
while (hasNext())
action.accept(next());
}
}
总结
- 方法数不同,Enumeration只有两个,Iterator有四个,Enumeration对操作对象是只读的,不支持其他操作,而Iteratior对操作对象可以执行移除操作
- Enumeration 是JDK 1.0添加的接口。使用到它的函数包括Vector、Hashtable等类,这些类都是JDK 1.0中加入的,Enumeration存在的目的就是为它们提供遍历接口。Enumeration本身并没有支持同步,而在Vector、Hashtable实现Enumeration时,添加了同步。
而Iterator 是JDK 1.2才添加的接口,它也是为了HashMap、ArrayList等集合提供遍历接口。Iterator是支持fail-fast机制的:当多个线程对同一个集合的内容进行操作时,就可能会产生fail-fast事件
Collection 和 Collections 区别
- java.util.Collection 是一个集合接口。它提供了对集合对象进行基本操作的通用接口方法。Collection接口在Java 类库中有很多具体的实现。Collection接口的意义是为各种具体的集合提供了最大化的统一操作方式
- java.util.Collections 是一个包装类。它包含有各种有关集合操作的静态多态方法。此类不能实例化,就像一个工具类,服务于Java的Collection框架。
//常用Collections方法
@Test
public void testCollections(){
List<Integer> list = Arrays.asList(1,3,2,5,9,8,6,5);
//Collections.sort(list);//排序,out:1,2,3,5,5,6,8,9
//int i = Collections.binarySearch(list, 5);//二分查找并范围匹配到第一个的索引,out:3
//Collections.emptyList();//获取一个空list
//Integer max = Collections.max(list);//获取当前集合中的最大值,out:9
//System.out.println(max);
Collections.rotate(list,4);//将当前集合从末尾开始截取指定长度的元素旋转到元素前面,out:[9, 8, 6, 5, 1, 3, 2, 5]
System.out.println(list);
}
fail-fast
fail-fast
介绍
java集合(Collection)中的一种错误机制。当多个线程对同一个集合的内容进行操作时,就可鞥产生fail-fast事件。如:当A线程通过Iterator遍历集合时,若当前集合的内容被B线程所改变,那A线程访问集合时,就会剖出ConcurrentModificationException异常,产生fail-fast事件。注:
只能用来检测错误,因为JDK并不保证fail-fast机制一定会发生
解决方法
使用Java.util.concurrent包下对应的类替换即可
示例及产生原理
示例
package test;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class TestFailFast {
//定义去全局变量
private static List<String> list = new ArrayList<>();
public static void main(String[] args) {
//开启第一个线程,往集合中依次添加1,2,3,4,5,6
new Thread(() -> {
for(int i = 1;i <= 6;i++){
list.add(i+"");
foreachList();
}
}).start();
//开启第二个线程,往集合中依次添加7,8,9,10,11
new Thread(() -> {
for(int i = 7;i <= 11;i++){
list.add(i+"");
foreachList();
}
}).start();
}
/**
* 遍历集合
*/
public static void foreachList(){
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()){
String str = iterator.next();
System.out.println(str);
}
}
}
/** 输出结果,第一个线程在遍历过程中,第二个线程往集合中添加了一个元素,抛出异常
* java.util.ConcurrentModificationException
* at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:909)
* at java.util.ArrayList$Itr.next(ArrayList.java:859)
* at test.TestString.foreachList(TestString.java:35)
* at test.TestString.lambda$main$1(TestString.java:23)
* at java.lang.Thread.run(Thread.java:748)
*/
产生原理
java8 ArrayList的Iterator 源码如下:
private class Itr implements Iterator<E> {
int cursor; // 下一个元素的索引
int lastRet = -1; // 最后一个元素的所有,如果没有则返回-1
/**
* 集合的每次增删改都会将modCount+1,每次创建Iterator是将modCount+1赋值给
* expectedModCount,用来记录当前迭代集合的修改次数,在上面示例中,第一个线程向集合添加时遍历,
* 获取迭代器的expectedModCount为6(假设),而第二个线程向集合中添加了一个元素,集合的modCount为6+1,
* 所有迭代遍历调用next()方法中都会先调用checkForComodification();来判断expectedModCount与modCount是否相等,
* 若不相等则抛出ConcurrentModificationException异常
*
*/
int expectedModCount = modCount;
Itr() {}
public boolean hasNext() {
return cursor != size;
}
@SuppressWarnings("unchecked")
public E next() {
checkForComodification();
int i = cursor;
if (i >= size)
throw new NoSuchElementException();
//获取当前集合的元素
Object[] elementData = ArrayList.this.elementData;
/*若下一个元素索引 大于 当前元素集合大小时,同样说明有其他线程修改了当前集合,也会抛出ConcurrentModificationException异常*/
if (i >= elementData.length)
throw new ConcurrentModificationException();
cursor = i + 1;
return (E) elementData[lastRet = i];
}
public void remove() {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try {
ArrayList.this.remove(lastRet);
cursor = lastRet;
lastRet = -1;
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
@Override
@SuppressWarnings("unchecked")
public void forEachRemaining(Consumer<? super E> consumer) {
Objects.requireNonNull(consumer);
final int size = ArrayList.this.size;
int i = cursor;
if (i >= size) {
return;
}
final Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length) {
throw new ConcurrentModificationException();
}
while (i != size && modCount == expectedModCount) {
consumer.accept((E) elementData[i++]);
}
// update once at end of iteration to reduce heap write traffic
cursor = i;
lastRet = i - 1;
checkForComodification();
}
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
fail-safe
在对任何集合结构的修改都会在一个复制的集合上进行修改,因此不会抛出ConcurrentModificationException,但会产生大量的无效对象,开销大,且无法保证读取的数据与原始数据中的数据一致,如Java.util.concurrent包下与集合对应的操作类几乎都是fail-safe的
Java 8 中 stream 相关用法
分成stream(串行流) 和 parallelStream(并行流)
获取stream的方法
stream的静态方法
- Stream.generate(() -> Arrays.asList(1,3,2,5,9,8,6,5))
- Stream.of(1,2,3,4,5,6,6)
- Stream.empty()
可迭代对象的stream方法 - Arrays.asList(1,3,2,5,9,8,6,5).stream()
基本用法
@Test
public void testStream(){
List<Integer> list = Arrays.asList(1,3,2,5,9,8,6,5);
//过滤出为偶数的元素并返回一个新的集合
List<Integer> filterList = list.stream().filter(item -> item % 2 == 0).collect(Collectors.toList());//2,8,6
//将list的每个元素都+1并返回新集合
List<Integer> mapList = list.stream().map(item -> item + 1).collect(Collectors.toList());//2,4,3,6,10,9,7,6
//打印集合的每个元素
list.stream().forEach(System.out::println);//1,3,2,5,9,8,6,5
//匹配当前列表的元素是否全为偶数,必须全部匹配时才返回true
boolean b = list.stream().allMatch(item -> item % 2 == 0);//false
//匹配当前列表的元素是否存在偶数,存在即返回true
boolean b1 = list.stream().anyMatch(item -> item % 2 == 0);//true
//获取当前流中的第一个元素,返回Optional
Optional<Integer> first = list.stream().findFirst();
first.get();//1
//去除流中的重复元素
List<Integer> distinctList = list.stream().distinct().collect(Collectors.toList());//1,3,2,5,9,8,6
//获取流中元素为5的个数
list.stream().filter(item -> item == 5).count();
//类似于数据库中limit start , count
list.stream().skip(1).limit(3);
//执行reduction在此流中的元素,使用所提供的身份,积累和组合功能
//第一个参数为上次执行的结果,item为当前元素值,返回值为Optional
Optional<Integer> reduce = list.stream().reduce((sum, item) -> item + sum);
System.out.println(reduce.get());
//第一个参数为初始值,若stream为null,则返回指定值,不返回Optional ,因为不存在null
Integer integer = list.stream().reduce(1, (sum, num) -> num + sum);
System.out.println(integer);
//第三个方法比第二个多了一个参数 BinaryOperator combiner,这个是在并发执行的时候用的,用来把多个accumulator的结果结合到一起。
Integer reduce1 = list.parallelStream().reduce(0, (sum, num) -> sum + num, (result1, result2) ->
result1 + result2);
System.out.println(reduce1);
}
collect用法
第一种用法
/**
* supplier 结果容器
* accumulator 执行操作容器
* 有两个参数 第一个参数:前一次的结果值
* 第二个参数:当前计算数值
* combiner: 合并策略 (只在并发情况下回调用)
* 第一个参数:前一次结果值,
第二个参数:当前计算后的容器值
*
*/
<R> R collect(Supplier<R> supplier,
BiConsumer<R, ? super T> accumulator,
BiConsumer<R, R> combiner);
public void testCollect(){
List<Integer> list = Arrays.asList(1,3,2,5,9,8,6,5);
ArrayList<Object> collect = list.parallelStream().collect(ArrayList::new, (arrayList, el) -> {
if(el % 2 != 0){
arrayList.add(el);
}
}, (res1, res2) -> {
res1.addAll(res2);
});
System.out.println(collect);//1,3,5,9,5
}
第二种用法
<R, A> R collect(Collector<? super T, A, R> collector);
class Person {
private String name;
private Integer age;
private Integer height;
public Person(String name, Integer age, Integer height) {
this.name = name;
this.age = age;
this.height = height;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public Integer getHeight() {
return height;
}
public void setHeight(Integer height) {
this.height = height;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
", height=" + height +
'}';
}
}
@Test
public void testStream(){
List<String> list = Arrays.asList("张三", "李四", "王五", "赵六");
//Collectors.joining() 无缝拼接
String s0 = list.stream().collect(Collectors.joining());
System.out.println(s0);//张三李四王五赵六
//Collectors.joining(参数为拼接时所用字符串)
String s = list.stream().collect(Collectors.joining(","));
System.out.println(s);//张三,李四,王五,赵六
//Collectors.joining(拼接字符串,拼接前缀,拼接后缀)
String s1 = list.stream().collect(Collectors.joining(",", "**", "&&"));
System.out.println(s1);//**张三,李四,王五,赵六&&
//Collectors.toList() 拼接成List集合 toSet()同理,知识转成Set类型
List<String> toList = list.stream().collect(Collectors.toList());
System.out.println(toList);//[张三, 李四, 王五, 赵六]
//Collectors.counting() 获取收集到元素的个数
Long len = list.stream().collect(Collectors.counting());
System.out.println(len);//4
//分组
List<Person> personList = new ArrayList<>();
Person person1 = new Person("张三",12,178);
Person person2 = new Person("李四",15,168);
Person person3 = new Person("王五",19,178);
Person person4 = new Person("赵六",21,198);
Person person5 = new Person("阿萨德",23,158);
Person person6 = new Person("王宝强",35,170);
Person person7 = new Person("假奶绿",30,165);
personList.add(person1);
personList.add(person2);
personList.add(person3);
personList.add(person4);
personList.add(person5);
personList.add(person6);
personList.add(person7);
//groupingBy(Function<? super T, ? extends K> classifier) 按指定key分组
Map<String, List<String>> collect = list.stream().collect(Collectors.groupingBy(item -> item+"1"));
System.out.println(collect);//{王五1=[王五], 赵六1=[赵六], 张三1=[张三], 李四1=[李四]}
//将年龄作为key,值为每个person
Map<Integer, List<Person>> collect1 = personList.stream().collect(Collectors.groupingBy(item -> item.getAge()));
System.out.println(collect1);//{35=[Person{name='王宝强', age=35, height=170}], 19=[Person{name='王五', age=19, height=178}], 21=[Person{name='赵六', age=21, height=198}], 23=[Person{name='阿萨德', age=23, height=158}], 12=[Person{name='张三', age=12, height=178}], 30=[Person{name='假奶绿', age=30, height=165}], 15=[Person{name='李四', age=15, height=168}]}
//将name作为key,并将值封装到set集合
Map<String, Set<Person>> setMap = personList.stream().collect(Collectors.groupingBy(item -> item.getName(), Collectors.toSet()));
System.out.println(setMap);//{李四=[Person{name='李四', age=15, height=168}], 张三=[Person{name='张三', age=12, height=178}], 王宝强=[Person{name='王宝强', age=35, height=170}], 王五=[Person{name='王五', age=19, height=178}], 假奶绿=[Person{name='假奶绿', age=30, height=165}], 赵六=[Person{name='赵六', age=21, height=198}], 阿萨德=[Person{name='阿萨德', age=23, height=158}]}
//指定key-value上限类型,将name作为key,并将值封装到Map集合
HashMap<String, Map<Integer, Integer>> collect2 = personList.stream().collect(Collectors.groupingBy(Person::getName, HashMap::new, Collectors.toMap(Person::getAge, Person::getHeight)));
System.out.println(collect2);//{李四={15=168}, 张三={12=178}, 王宝强={35=170}, 王五={19=178}, 假奶绿={30=165}, 赵六={21=198}, 阿萨德={23=158}}
}