https://www.cnblogs.com/yangsanluo/p/14976907.html:通过stream——>list转map
https://nimbletext.com/Live——批量sql
1. Lambda表达式
1.1 需求分析
创建一个新的线程,指定线程要执行的任务
public static void main(String[] args) {
// 开启一个新的线程
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("新线程中执行的代码 :
"+Thread.currentThread().getName());
}
}).start();
System.out.println("主线程中的代码:" + Thread.currentThread().getName());
代码分析:
1. Thread类需要一个Runnable接口作为参数,其中的抽象方法run方法是用来指定线程任务内容的
核心
2. 为了指定run方法体,不得不需要Runnable的实现类
3. 为了省去定义一个Runnable 的实现类,不得不使用匿名内部类
4. 必须覆盖重写抽象的run方法,所有的方法名称,方法参数,方法返回值不得不都重写一遍,而且
不能出错,
5. 而实际上,我们只在乎方法体中的代码
1.2 Lambda表达式初体验
Lambda表达式是一个匿名函数,可以理解为一段代码的传递
new Thread(() ->
{ System.out.println("新线程Lambda表达式..." +Thread.currentThread().getName());
}) .start();
Lambda表达式的优点:Lambda表达式简化了匿名内部类的使用,语法更加的简单
1.3 Lambda表达式的语法规则
Lambda表达式省去了面向对象的条条框框,Lambda的标准格式分为3部分
(参数类型 参数名称)->{
代码体;
};
- (参数类型 参数名称):参数列表
- {代码体}:方法体
-
1.3.1 Lambda表达式练习01
定义接口UserService
public interface UserService {
void show();
}
//Lambda表达式练习01,无参数无返回值
@Test
public void testLambda02() {
goShow(new UserService() {
@Override
public void show() {
System.out.println("show方法执行了");
}
});
goShow(()->{
System.out.println("Lambda Show方法执行了");
});
}
private static void goShow(UserService userService) {
userService.show();
}
1.3.2 Lambda表达式练习02
我们发现在sort方法的第二个参数是一个Comparator接口的匿名内部类,且执行的方法有参数和返回
值,那么我们可以改写为Lambda表达式//练习02:完成一个有参且有返回值得Lambda表达式案例
//创建一个Person对象
@Test
public void testLambda03(){
List<Person> list = new ArrayList<>();
list.add(new Person("周杰伦", 33, 175));
list.add(new Person("刘德华", 43, 185));
list.add(new Person("周星驰", 38, 177));
list.add(new Person("郭富城", 23, 170));
// Collections.sort(list, new Comparator<Person>() {
// @Override
// public int compare(Person o1, Person o2) {
// return o2.getAge()-o1.getAge();
// }
// });
// for (Person p :list) {
// System.out.println(p);
// }
Collections.sort(list,(Person o1, Person o2)->{
return o1.getAge()-o2.getAge();
});
for (Person p :list) {
System.out.println(p);
}
}
1.4 @FunctionalInterface注解
```java /**
- @FunctionalInterface
- 这是一个标志注解,被该注解修饰的接口只能声明一个抽象方法
*/
@FunctionalInterface
public interface UserService {
void show();
}
还可以通过反编译工具来查看生成的代码 XJad 工具来查看<a name="OX61m"></a>
##### 1.5 Lambda表达式的原理
匿名内部类的本质是在编译时生成一个Class 文件。XXXXX$1.class
```java
public class Demo01Lambda {
public static void main(String[] args) {
// 开启一个新的线程 new Thread(new Runnable() {
@Override public void run() {
System.out.println("新线程中执行的代码 : "+Thread.currentThread().getName());
}
}).start();
System.out.println("主线程中的代码:" + Thread.currentThread().getName());
System.out.println("---------------");
/*new Thread(() -> { System.out.println("新线程Lambda表达式..." +Thread.currentThread().getName()); }) .start();
*/
}
}
那么Lambda表达式的原理是什么呢?我们也通过反编译工具来查看static class Demo01Lambda$1 implements Runnable {
public void run() {
System.out.println((new StringBuilder()).append("新线程中执行的代码 : " ).append(Thread.currentThread().getName()).toString());
}
Demo01Lambda$1() {
}
}
1.6 Lambda表达式的省略写法
在lambda表达式的标准写法基础上,可以使用省略写法的规则为:
1. 小括号内的参数类型可以省略
2. 如果小括号内有且仅有一个参数,则小括号可以省略
3. 如果大括号内有且仅有一个语句,可以同时省略大括号,return 关键字及语句分号。public class Demo05Lambda {
public static void main(String[] args) {
goStudent((String name,Integer age)->{
return name+age+" 6666 ...";
});
// 省略写法
goStudent((name,age)-> name+age+" 6666 ...");
System.out.println("------");
goOrder((String name)->{
System.out.println("--->" + name); return 666;
});
// 省略写法
goOrder(name -> {
System.out.println("--->" + name); return 666;
});
goOrder(name -> 666);
}
public static void goStudent(StudentService studentService){
studentService.show("张三",22);
}
public static void goOrder(OrderService orderService){
orderService.show("李四");
}
}
1.7 Lambda表达式的使用前提
Lambda表达式的语法事非常简洁的,但是不是随便使用的,使用时有几个条件要特别注意:
1.方法的参数或局部变量类型必须为接口才能使用Lambda表达式
2.接口中有且只有一个抽象方法(@FunctionalInterface)1.8 Lambda和匿名内部类的对比
Lambda和匿名内部类的对比
1.所属类型不一样
- 匿名内部类的类型可以时类,抽象类,接口
- Lambda表达式需要的类型必须是接口
2.抽象方法的数量不一样
- 匿名内部类所需的接口中的抽象方法的数量是随意的
- Lambda表达式所需的接口中只能有一个抽象方法
3.实现原理不一样
- 匿名内部类是再编译后形成的一个class
-
2. 函数式接口
3. 方法引用与构造器引用
4. Stream Api
4.1 集合处理数据的弊端
当我们在需要对集合中的元素操作的时候,除了必须的添加,删除,获取外,最典型的操作就是集合的遍历
public class StreamTest01 {
public static void main(String[] args) {
// 定义一个List集合
List<String> list = Arrays.asList("张三","张三丰","成龙","周星驰");
// 1.获取所有 姓张的信息
List<String> list1 = new ArrayList<>();
for (String s : list) {
if(s.startsWith("张")){
list1.add(s);
}
}
// 2.获取名称长度为3的用户
List<String> list2 = new ArrayList<>();
for (String s : list1) {
if(s.length() == 3){
list2.add(s);
}
}
// 3. 输出所有的用户信息
for (String s : list2) {
System.out.println(s);
}
}
}
上面的代码针对我们不同的需求一次次的循环,这时我们希望有更加高效的处理方式,这时我们就可以通过JDK1.8中提供的Stream API来解决这个问题
Stream流过滤filter,配合forEach使用//通过Stream流过滤filter,配合forEach使用-->list
@Test
public void testStreamFilter(){
// 定义一个List集合
List<String> list = Arrays.asList("张三","张三丰","成龙","周星驰");
// 1.获取所有 姓张的信息
// 2.获取名称长度为3的用户
// 3. 输出所有的用户信息
list.stream().filter(s->s.startsWith("张"))
.filter(s->s.length() == 3)
.forEach(s->{
System.out.println(s);
});
}
上面的Stream Api 代码的含义:获取流,过滤张,过滤长度,逐一打印,代码相比于上面的案例更加的简洁
4.2 Stream流式思想概述
注意:Stream和IO流(InputStream/OutputStream)没有任何关系,请暂时忘记对传统IO流的固有印
象!
Stream流式思想类似于工厂车间的“生产流水线”,Stream流不是一种数据结构,不保存数据,而是对数
据进行加工 处理。Stream可以看作是流水线上的一个工序。在流水线上,通过多个工序让一个原材料加工成一个商品4.3 Stream流的获取方式
4.3.1 根据Collection获取
首先,java.util.Collection 接口中加入了default方法stream,也就是说Collection接口下所有的实现都可以通过stream方法来获取Stream流
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.stream();
Set<String> set = new HashSet<>();
set.stream();
Vector vector = new Vector();
vector.stream();
}
但是Map接口没有实现Collection接口 ,那这时怎么办呢?这时我们根据Map获取对应的key-value的集合
public static void main(String[] args) {
Map<String,Object> map = new HashMap<>();
Stream<String> stream = map.keySet().stream(); // key
Stream<Object> stream1 = map.values().stream(); // value
Stream<Map.Entry<String, Object>> stream2 = map.entrySet().stream(); // entry
}
4.3.2 通过Stream的of方法
在实际开发中我们通过不可避免的还是会操作到数组中的数据,由于数组对象不可能添加默认方法,所以Stream接口中提供了静态方法of
//Stream 中的静态方法Of
@Test
public void testStreamOf(){
Stream<String> a1 = Stream.of("a1", "a2", "a3");
String[] arr1 = {"aa","bb","cc"};
Stream<String> arr11 = Stream.of(arr1);
Integer[] arr2 = {1,2,3,4};
Stream<Integer> arr21 = Stream.of(arr2);
arr21.forEach(ss->{
System.out.println(ss);
});
// 注意:基本数据类型的数组是不行的
int[] arr3 = {1,2,3,4};
Stream.of(arr3).forEach(s->{
System.out.println(s);
});
}
4.4 Stream常用方法介绍
Stream 常用方法:
Stream流模型的操作很丰富,这里介绍一些常用的API。这些方法可以分成两种:
方法名 | 方法作用 | 返回值类型 | 方法种类 |
---|---|---|---|
count | 统计个数 | long | 终结 |
forEach | 逐一处理 | void | 终结 |
filter | 过滤 | Stream | 函数拼接 |
limit | 取用前几个 | Stream | 函数拼接 |
skip | 跳过前几个 | Stream | 函数拼接 |
map | 映射 | Stream | 函数拼接 |
concat | 组合 | Stream | 函数拼接 |
终结方法:返回值类型不再时Stream类型的方法,不再支持链式调用。本小节中,终结方法包含count和forEach方法。
非终结方法:返回值类型仍然是Stream类型的方法,支持链式调用。(除了终结方法外,其余方法均为非终结方法。)
Stream注意事项(重要):
- Stream只能操作一次
- Stream方法返回的是新的流
- Stream不调用终结方法,中间的操作不会执行
4.4.1 forEach方法
forEach用来遍历流中的数据的
该方法接受一个Consumer接口,会将每一个流元素交给函数处理//forEach方法
void forEach(Consumer<? super T> action);
//forEach方法
@Test
public void testForEach() {
List list = new ArrayList<>();
Stream.of("1","2","3").forEach(s->{
list.add(s);
});
list.stream().forEach(System.out::println);
}
4.4.2 count方法
Stream流中的count方法用来统计其中的元素个数的
该方法返回一个long值,代表元素的个数long count();
//count计数
@Test
public void testCount() {
long count = Stream.of("2", "3", "4").count();
List<Object> list = new ArrayList<>();
list.add("6");
list.add("8");
list.add("9");
list.add("10");
long count1 = list.stream().count();
System.out.println(count1);
}
4.4.3 filter方法
filter方法的作用是用来过滤数据的。返回符合条件的数据
可以通过filter方法将一个流转换成另一个子集流
该接口接收一个Predicate函数式接口参数作为筛选条件Stream<T> filter(Predicate<? super T> predicate);
//filter
@Test
public void testFilter() {
List<String> list = new ArrayList<>();
list.add("a6");
list.add("a8");
list.add("a9");
list.add("b10");
list.stream().filter(s->s.contains("a"))//包含a的全部筛选出来
.forEach(s->{
System.out.println(s);
});
}
4.4.4 limit方法 取前几个
limit方法可以对流进行截取处理,只取前几个数据,得到一个处理后的流
参数是一个long类型的数值,如果集合当前长度大于参数就进行截取,否则不操作:Stream<T> limit(long maxSize);
//limit 方法截取前几个数据
@Test
public void testLimit() {
List<String> list = new ArrayList<>();
list.add("a6");
list.add("a8");
list.add("a9");
list.add("b10");
list.stream().limit(2).forEach(s->{
System.out.println(s);
});
}
//结果:a6,a8
4.4.5 skip方法 跳过前几个
如果希望跳过前面几个元素,可以使用skip方法获取一个截取之后的流
实现:Stream<T> skip(long n);
//skip 方法截取前几个数据
@Test
public void testSkip() {
List<String> list = new ArrayList<>();
list.add("a6");
list.add("a8");
list.add("a9");
list.add("b10");
list.stream().skip(2).forEach(s->{
System.out.println(s);
});
}
//结果:a9,a10
4.4.6 map方法
如果我们需要将流中的元素映射到另一个流中,可以使用map这个方法:<R> Stream<R> map(Function<? super T, ? extends R> mapper);
该接口需要一个Function函数式接口参数,可以将当前流中的T类型数据转换为另一种R类型的数据//map 方法映射
@Test
public void testMap() {
List<String> list = new ArrayList<>();
list.add("6");
list.add("8");
list.add("9");
list.add("10");
list.stream().map(s->Integer.parseInt(s))
.forEach(System.out::println);
}
//结果:String集合映射成Integer集合
4.4.7 sorted排序方法
如果需要将数据排序,可以使用sorted方法:
在使用的时候可以根据自然规则排序,也可以通过比较强来指定对应的排序规则Stream<T> sorted();
//sorted 方法映射
@Test
public void testSorted() {
List<String> list = new ArrayList<>();
list.add("88");
list.add("8");
list.add("9");
list.add("10");
list.stream().map(s->Integer.parseInt(s))
.sorted((s1,s2)->{return s1-s2;})//指定的方式进行排序,从小到大
.forEach(System.out::println);
}
4.4.8 distinct去重方法
如果要去掉重复数据,可以使用distinct方法:Stream<T> distinct();
实现:
注意:Stream使用distinct去重基本数据类型可以直接去重,但是自定类型需要重写hashCode和equals方法来移除重复的元素。//distinct 去重的方法
@Test
public void testDistinct() {
List<String> list = new ArrayList<>();
list.add("8");
list.add("8");
list.add("9");
list.add("10");
list.stream().map(s->Integer.parseInt(s))
.sorted((s1,s2)->{return s1-s2;})
.distinct()
.forEach(System.out::println);
System.out.println("=======================");
//自定义的实体类过滤
List<Person> personList = new ArrayList<>();
personList.add(new Person("周杰伦", 33, 175));
personList.add(new Person("刘德华", 43, 185));
personList.add(new Person("周星驰", 38, 177));
personList.add(new Person("周杰伦", 33, 175));
personList.stream().distinct()
.forEach(System.out::println);
}
4.4.9 match 方法 条件判断
实现:boolean anyMatch(Predicate<? super T> predicate); // 元素是否有任意一个满足条件
boolean allMatch(Predicate<? super T> predicate); // 元素是否都满足条件
boolean noneMatch(Predicate<? super T> predicate); // 元素是否都不满足条件
注意:match是个终结方法public static void main(String[] args) {
boolean b = Stream.of("1", "3", "3", "4", "5", "1", "7")
.map(Integer::parseInt)
//.allMatch(s -> s > 0)
//.anyMatch(s -> s >4)
.noneMatch(s -> s > 4) ;
System.out.println(b);
}
4.4.10 find方法 找其中某一个
如果我们需要找到某些数据,可以使用find方法来实现Optional<T> findFirst(); //找到第一个返回
Optional<T> findAny(); //找到一个就返回
实现:public static void main(String[] args) {
Optional<String> first = Stream.of("1", "3", "3", "4", "5", "1", "7")
.findFirst();
System.out.println(first.get());
Optional<String> any = Stream.of("1", "3", "3", "4", "5", "1", "7")
.findAny();
System.out.println(any.get());
}
4.4.11 max和min方法
取最大值和最小值
实现:Optional<T> min(Comparator<? super T> comparator);
Optional<T> max(Comparator<? super T> comparator);
public static void main(String[] args) {
Optional<Integer> max = Stream.of("1", "3", "3", "4", "5", "1", "7")
.map(Integer::parseInt)
.max((o1,o2)->o1-o2);
System.out.println(max.get());
Optional<Integer> min = Stream.of("1", "3", "3", "4", "5", "1", "7")
.map(Integer::parseInt)
.min((o1,o2)->o1-o2);
System.out.println(min.get()); }
4.4.412 reduce方法
如果需要将所有数据归纳得到一个数据,可以使用reduce方法
实现:T reduce(T identity, BinaryOperator<T> accumulator);
public static void main(String[] args) {
Integer sum = Stream.of(4, 5, 3, 9)
// identity默认值
// 第一次的时候会将默认值赋值给x
// 之后每次会将 上一次的操作结果赋值给x y就是每次从数据中获取的元素
.reduce(0, (x, y) -> {
System.out.println("x="+x+",y="+y);
return x + y;
});
System.out.println(sum);
// 获取 最大值
Integer max = Stream.of(4, 5, 3, 9)
.reduce(0, (x, y) -> {
return x > y ? x : y;
});
System.out.println(max);
}
4.4.13 concat两个流合并
如果有两个流,希望合并成为一个流,那么可以使用Stream接口的静态方法concat
实现:public static <T> Stream<T> concat(Stream<? extends T> a, Stream<? extends T> b) {
Objects.requireNonNull(a);
Objects.requireNonNull(b);
@SuppressWarnings("unchecked")
Spliterator<T> split = new Streams.ConcatSpliterator.OfRef<>(
(Spliterator<T>) a.spliterator(), (Spliterator<T>)
b.spliterator());
Stream<T> stream = StreamSupport.stream(split, a.isParallel() || b.isParallel());
return stream.onClose(Streams.composedClose(a, b));
}
public static void main(String[] args) {
Stream<String> stream1 = Stream.of("a","b","c");
Stream<String> stream2 = Stream.of("x", "y", "z");
// 通过concat方法将两个流合并为一个新的流
Stream.concat(stream1,stream2).forEach(System.out::println);
}
4.4.14 综合案例
定义两个集合,然后在集合中存储多个用户名称。然后完成如下的操作:
1. 第一个队伍只保留姓名长度为3的成员
2. 第一个队伍筛选之后只要前3个人
3. 第二个队伍只要姓张的成员
4. 第二个队伍筛选之后不要前两个人
5. 将两个队伍合并为一个队伍
6. 根据姓名创建Person对象
7. 打印整个队伍的Person信息/**
* 综合案例:
* 1. 第一个队伍只保留姓名长度为3的成员
* 2. 第一个队伍筛选之后只要前3个人
* 3. 第二个队伍只要姓张的成员
* 4. 第二个队伍筛选之后不要前两个人
* 5. 将两个队伍合并为一个队伍
* 6. 根据姓名创建Person对象
* 7. 打印整个队伍的Person信息
*/
@Test
public void testPerson() {
List<String> list1 = Arrays.asList("迪丽热巴", "宋远桥", "苏星河", "老子", "庄子", "孙子", "洪七 公");
List<String> list2 = Arrays.asList("古力娜扎", "张无忌", "张三丰", "赵丽颖", "张二狗", "张天爱", "张三");
// 1. 第一个队伍只保留姓名长度为3的成员
// 2. 第一个队伍筛选之后只要前3个人
Stream<String> stream1 = list1.stream().filter(s -> s.length() == 3)
.limit(3);
// 3. 第二个队伍只要姓张的成员
// 4. 第二个队伍筛选之后不要前两个人
Stream<String> stream2 = list2.stream().filter(s -> s.startsWith("张"))
.skip(2);
//5. 将两个队伍合并为一个队伍
//6. 根据姓名创建Person对象
//7. 打印整个队伍的Person信息
Stream.concat(stream1,stream2)
.map(s->new Person(s))
.forEach(p->{
System.out.println(p);
});
}
4.5 Stream 结果收集
4.5.1 结果收集到集合中
/**
* Stream结果收集
* 收集到集合中
*/
@Test
public void test01(){
// Stream<String> stream = Stream.of("aa", "bb", "cc");
List<String> list = Stream.of("aa", "bb", "cc","aa")
.collect(Collectors.toList());
System.out.println(list);
// 收集到 Set集合中
Set<String> set = Stream.of("aa", "bb", "cc", "aa")
.collect(Collectors.toSet());
System.out.println(set);
// 如果需要获取的类型为具体的实现,比如:
ArrayList HashSet ArrayList<String> arrayList = Stream.of("aa", "bb", "cc", "aa")
//.collect(Collectors.toCollection(() -> new ArrayList<>()));
.collect(Collectors.toCollection(ArrayList::new));
System.out.println(arrayList);
HashSet<String> hashSet = Stream.of("aa", "bb", "cc", "aa")
.collect(Collectors.toCollection(HashSet::new));
System.out.println(hashSet);
}
4.5.2 结果收集到数组中
Stream中提供了toArray方法来将结果放到一个数组中,返回值类型是Object[],如果我们要指定返回的
类型,那么可以使用另一个重载的toArray(IntFunction f)方法/**
* Stream结果收集到数组中
*/
@Test
public void test02(){
Object[] objects = Stream.of("aa", "bb", "cc", "aa")
.toArray(); // 返回的数组中的元素是 Object类型
System.out.println(Arrays.toString(objects));
// 如果我们需要指定返回的数组中的元素类型
String[] strings = Stream.of("aa", "bb", "cc", "aa")
.toArray(String[]::new);
System.out.println(Arrays.toString(strings));
}
4.6 并行的Stream流
4.6.1 串行的Stream流
我们前面使用的Stream流都是串行,也就是在一个线程上面执行。/**
* 串行流
*/
@Test
public void test01(){
Stream.of(5,6,8,3,1,6)
.filter(s->{
System.out.println(Thread.currentThread() + "" + s);
return s > 3;
}).count();
}
响应结果:
Thread[main,5,main]5
Thread[main,5,main]6
Thread[main,5,main]8
Thread[main,5,main]3
Thread[main,5,main]1
Thread[main,5,main]6
4.6.2 并行流
parallelStream其实就是一个并行执行的流,它通过默认的ForkJoinPool,可以提高多线程任务的速
度。
- 获取并行流
我们可以通过两种方式来获取并行流。
1. 通过List接口中的parallelStream方法来获取
2. 通过已有的串行流转换为并行流(parallel)
实现:
/**
* 获取并行流的两种方式
*/
@Test
public void test02(){
List<Integer> list = new ArrayList<>();
// 通过List 接口 直接获取并行流
Stream<Integer> integerStream = list.parallelStream();
// 将已有的串行流转换为并行流
Stream<Integer> parallel = Stream.of(1, 2, 3).parallel();
}
- 并行流操作
效果:/**
* 并行流操作
*/
@Test
public void test03(){
Stream.of(1,4,2,6,1,5,9)
.parallel() // 将流转换为并发流,Stream处理的时候就会通过多线程处理
.filter(s->{
System.out.println(Thread.currentThread() + " s=" +s);
return s > 2;
}).count();
}
Thread[main,5,main] s=1
Thread[ForkJoinPool.commonPool-worker-2,5,main] s=9
Thread[ForkJoinPool.commonPool-worker-6,5,main] s=6
Thread[ForkJoinPool.commonPool-worker-13,5,main] s=2
Thread[ForkJoinPool.commonPool-worker-9,5,main] s=4
Thread[ForkJoinPool.commonPool-worker-4,5,main] s=5
Thread[ForkJoinPool.commonPool-worker-11,5,main] s=1
4.6.3 并行流和串行流对比
我们通过for循环,串行Stream流,并行Stream流来对500000000亿个数字求和。来看消耗时间
通过案例我们可以看到parallelStream的效率是最高的。public class Test03 {
private static long times = 500000000;
private long start;
@Before
public void befor(){
start = System.currentTimeMillis();
}
@After
public void end(){
long end = System.currentTimeMillis();
System.out.println("消耗时间:" + (end - start));
}
/**
* 普通for循环 消耗时间:138
*/
@Test
public void test01(){
System.out.println("普通for循环:");
long res = 0;
for (int i = 0; i < times; i++) {
res += i;
}
}
/**
* 串行流处理
* 消耗时间:203
*/
@Test
public void test02(){
System.out.println("串行流:serialStream");
LongStream.rangeClosed(0,times) .reduce(0,Long::sum);
}
/**
* 并行流处理 消耗时间:84
*/
@Test
public void test03(){
LongStream.rangeClosed(0,times) .parallel() .reduce(0,Long::sum);
}
}
Stream并行处理的过程会分而治之,也就是将一个大的任务切分成了多个小任务,这表示每个任务都是
一个线程操作。4.6.4 线程安全问题
在多线程的处理下,肯定会出现线程安全问题。如下:
运行结果:@Test
public void test01(){
List<Integer> list = new ArrayList<>();
for (int i = 0; i < 1000; i++) {
list.add(i);
}
System.out.println(list.size());
List<Integer> listNew = new ArrayList<>();
// 使用并行流来向集合中添加数据
list.parallelStream()
//.forEach(s->listNew.add(s));
.forEach(listNew::add);
System.out.println(listNew.size());
}
或者直接抛异常839
针对这个问题,我们的解决方案有哪些呢?java.lang.ArrayIndexOutOfBoundsException
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorI mpl.java:62)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorA ccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
at java.util.concurrent.ForkJoinTask.getThrowableException(ForkJoinTask.java:598)
....
Caused by: java.lang.ArrayIndexOutOfBoundsException: 366
at java.util.ArrayList.add(ArrayList.java:463)
1. 加同步锁
2. 使用线程安全的容器
3. 通过Stream中的toArray/collect操作
实现:/**
* 加同步锁
*/
@Test
public void test02(){
List<Integer> listNew = new ArrayList<>();
Object obj = new Object();
IntStream.rangeClosed(1,1000)
.parallel()
.forEach(i->{
synchronized (obj){
listNew.add(i);
}
});
System.out.println(listNew.size());
}
/**
* 使用线程安全的容器
*/
@Test
public void test03(){
Vector v = new Vector();
Object obj = new Object();
IntStream.rangeClosed(1,1000)
.parallel()
.forEach(i->{
synchronized (obj){
v.add(i);
}
});
System.out.println(v.size());
}
/**
* 将线程不安全的容器转换为线程安全的容器
*/
@Test
public void test04(){
List<Integer> listNew = new ArrayList<>();
// 将线程不安全的容器包装为线程安全的容器
List<Integer> synchronizedList = Collections.synchronizedList(listNew);
Object obj = new Object();
IntStream.rangeClosed(1,1000)
.parallel()
.forEach(i->{
synchronizedList.add(i);
});
System.out.println(synchronizedList.size());
}
/**
* 我们还可以通过Stream中的 toArray方法或者 collect方法来操作
* 就是满足线程安全的要求
*/
@Test
public void test05(){
List<Integer> listNew = new ArrayList<>();
Object obj = new Object();
List<Integer> list = IntStream.rangeClosed(1, 1000)
.parallel()
.boxed()
.collect(Collectors.toList());
System.out.println(list.size());
}
5. 接口中的默认方法与静态方法
6. 新时间日期APi
7. 其他新特性