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() {@Overridepublic 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,无参数无返回值@Testpublic void testLambda02() {goShow(new UserService() {@Overridepublic 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对象@Testpublic 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```javapublic 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@Testpublic 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(); // keyStream<Object> stream1 = map.values().stream(); // valueStream<Map.Entry<String, Object>> stream2 = map.entrySet().stream(); // entry}
4.3.2 通过Stream的of方法
在实际开发中我们通过不可避免的还是会操作到数组中的数据,由于数组对象不可能添加默认方法,所以Stream接口中提供了静态方法of
//Stream 中的静态方法Of@Testpublic 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方法@Testpublic 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计数@Testpublic 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@Testpublic 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 方法截取前几个数据@Testpublic 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 方法截取前几个数据@Testpublic 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 方法映射@Testpublic 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 方法映射@Testpublic 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 去重的方法@Testpublic 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信息*/@Testpublic 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结果收集* 收集到集合中*/@Testpublic 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结果收集到数组中*/@Testpublic 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流都是串行,也就是在一个线程上面执行。/*** 串行流*/@Testpublic 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]5Thread[main,5,main]6Thread[main,5,main]8Thread[main,5,main]3Thread[main,5,main]1Thread[main,5,main]6
4.6.2 并行流
parallelStream其实就是一个并行执行的流,它通过默认的ForkJoinPool,可以提高多线程任务的速
度。
- 获取并行流
我们可以通过两种方式来获取并行流。
1. 通过List接口中的parallelStream方法来获取
2. 通过已有的串行流转换为并行流(parallel)
实现:
/*** 获取并行流的两种方式*/@Testpublic void test02(){List<Integer> list = new ArrayList<>();// 通过List 接口 直接获取并行流Stream<Integer> integerStream = list.parallelStream();// 将已有的串行流转换为并行流Stream<Integer> parallel = Stream.of(1, 2, 3).parallel();}
- 并行流操作
效果:/*** 并行流操作*/@Testpublic 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=1Thread[ForkJoinPool.commonPool-worker-2,5,main] s=9Thread[ForkJoinPool.commonPool-worker-6,5,main] s=6Thread[ForkJoinPool.commonPool-worker-13,5,main] s=2Thread[ForkJoinPool.commonPool-worker-9,5,main] s=4Thread[ForkJoinPool.commonPool-worker-4,5,main] s=5Thread[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;@Beforepublic void befor(){start = System.currentTimeMillis();}@Afterpublic void end(){long end = System.currentTimeMillis();System.out.println("消耗时间:" + (end - start));}/*** 普通for循环 消耗时间:138*/@Testpublic void test01(){System.out.println("普通for循环:");long res = 0;for (int i = 0; i < times; i++) {res += i;}}/*** 串行流处理* 消耗时间:203*/@Testpublic void test02(){System.out.println("串行流:serialStream");LongStream.rangeClosed(0,times) .reduce(0,Long::sum);}/*** 并行流处理 消耗时间:84*/@Testpublic void test03(){LongStream.rangeClosed(0,times) .parallel() .reduce(0,Long::sum);}}
Stream并行处理的过程会分而治之,也就是将一个大的任务切分成了多个小任务,这表示每个任务都是
一个线程操作。4.6.4 线程安全问题
在多线程的处理下,肯定会出现线程安全问题。如下:
运行结果:@Testpublic 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.ArrayIndexOutOfBoundsExceptionat 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: 366at java.util.ArrayList.add(ArrayList.java:463)
1. 加同步锁
2. 使用线程安全的容器
3. 通过Stream中的toArray/collect操作
实现:/*** 加同步锁*/@Testpublic 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());}/*** 使用线程安全的容器*/@Testpublic 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());}/*** 将线程不安全的容器转换为线程安全的容器*/@Testpublic 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方法来操作* 就是满足线程安全的要求*/@Testpublic 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. 其他新特性
