更新于 2022-6-1 15:32
文章目录
简介
lambda表达式
lambda练习一:
lambda练习二:
省略写法
接口中新增的方法
1.JDK8中接口的新增
2.默认方法
接口默认方法的格式
接口中默认方法的使用
3.静态方法
语法规则
接口中静态方法的使用
两者的区别介绍
函数式接口
函数式接口的由来
接口介绍
Supplier
Consumer
Function
Predicate
方法引用与构造器引用
Stream API
1.集合处理数据的弊端
核心思想
Stream的两种获取方式
根据Collection获取
通过Stream的of方法获取
Stream常用方法介绍
forEach
count
filter
limit
skip
map
sorted
distinct
match
find
max和min
reduce
map和reduce的组合
mapToInt(mapToDoub,mapToLong)
concat
综合案例
结果收集
结果收集到集合
结果收集到数组
对流中的数据做聚合计算(最大值、求和、平均值等)
分组计算
对流中的数据做分区操作
对流中的数据做拼接
并行的Stream流
串行流
创建并行流
并行流操作
并行流比较串行流
线程安全问题
Fork/Join框架
Optional类
创建Optional
基本方法介绍和使用
高级用法
新时间日期API
其他新特性
简介
速度更快
代码更少(增加了新的语法Lambda表达式)
强大的Stream API
便于并行
最大化减少空指针异常Optional
lambda表达式
new Thread(new Runnable() {@Overridepublic void run() {System.out.println("新线程中执行的代码" + Thread.currentThread().getName());}}).start();
//开启一个新线程
new Thread(() -> System.out.println("新线程中执行的代码"+Thread.currentThread().getName()));
简化了代码 ->
lambda省去了面向对象的条条框框,Lambda的标准格式由3个部分组成
(参数类型 参数名称) -> {
代码体;
}
lambda练习一:
先创建一个UserService接口 无返回的方法
public interface UserService {/*** wwu*/void show();
}
然后
public static void main(String[] args) {goShow(new UserService() {@Overridepublic void show() {System.out.println("显示");}});goShow(() -> System.out.println("Hello"));}public static void goShow(UserService userService){userService.show();}
lambda练习二:
先创建一个实体类
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Person {private String name;private Integer age;private Integer height;}
然后
public class Demo04 {public static void main(String[] args) {List<Person> list = new ArrayList<>();list.add(new Person("周杰伦",18,180));list.add(new Person("刘德华",35,180));list.add(new Person("张学友",21,180));list.add(new Person("周星驰",25,180));list.add(new Person("周润发",31,180));Collections.sort(list, new Comparator<Person>() {@Overridepublic int compare(Person o1, Person o2) {return o1.getAge() - o2.getAge();}});for(Person person : list){System.out.println(person);}System.out.println("------------");list.sort(Comparator.comparingInt(Person::getAge));list.forEach(System.out::println);}}
省略写法
先写两个接口
public interface OrderService {Integer show(String name);
}
public interface StudentService {String show(String name,Integer age);
}
然后
public class Demo05 {public static void main(String[] args) {goStudent((String name,Integer age)->{return name + age +"666...";});// 省略写法goStudent((name, age) ->name+age+"666");System.out.println("--------");geOrder((String name)->{System.out.println("--->" + name);return 999;});// 省略写法geOrder(name -> 999);}public static void goStudent(StudentService service){service.show("张三",22);}public static void geOrder(OrderService orderService){orderService.show("李四");}
}
接口中新增的方法
1.JDK8中接口的新增
在JDK8中针对接口有增强,在JDK8之前:
interface 接口名{静态常量;抽象方法;
}
JDK8之后对接口做了增强,接口中可以有默认方法和静态方法
interface 接口名{静态常量;抽象方法;默认方法;静态方法;
}
2.默认方法
interface A {void test1();void test2();
}class B implements A {@Overridepublic void test1() {}@Overridepublic void test2() {}
}
接口新增方法,实现的类也要去实现方法
不方便,麻烦
接口默认方法的格式
interface 接口名{修饰符 default 返回值类型 方法名{}}
接口中默认方法的使用
1.实现类直接调用接口的默认方法
2.实现类重写接口的默认方法
public class Demo01 {public static void main(String[] args) {A a = new B();A c = new C();// 实现类重写接口的默认方法a.test3();// 实现类直接调用接口的默认方法c.test3();}}interface A {void test1();void test2();/*** 接口中定义的默认方法* @return*/public default String test3(){System.out.println("接口中的默认方法执行了。。。。");return "hello";}
}class B implements A {@Overridepublic void test1() {}@Overridepublic void test2() {}@Overridepublic String test3() {System.out.println("B 实现类中重写了默认方法。。");return "ok...";}
}class C implements A {@Overridepublic void test1() {}@Overridepublic void test2() {}
}
3.静态方法
JDK8中为接口新增了静态方法,作用也是为了接口的扩展
语法规则
interface 接口名{
修饰符 static 返回值类型 方法名{
方法体;
}
}
接口中静态方法的使用
public static String test4(){System.out.println("接口中的静态方法。。。");return "hello";}
接口中的静态方法是不能够被重写的。
两者的区别介绍
1.默认方法通过实例调用,静态方法通过接口名调用
2.默认方法可以被继承,实现类可以直接调用接口默认方法,也可以重写接口默认方法
3.静态方法不能被继承,实现类不能重写接口的静态方法,只能使用接口名调用
函数式接口
函数式接口的由来
使用Lambda表达式的前提是需要有函数式接口,而Lambda表达式使用时不关心接口名,只关心参数列表和返回值类型,为了使用Lambda表达式更加方便,在JDK8中提供了大量的常用函数式接口
public class Demo {public static void main(String[] args) {fun1(arr -> {int sum = 0;for (int i : arr) {sum += i;}return sum;});}public static void fun1(Operator operator){int[] arr = {1,2,3,4};int sum = operator.getSum(arr);System.out.println("sum = " + sum);}}/*** 函数式接口*/
@FunctionalInterface
interface Operator{int getSum(int[] arr);
}
接口介绍
Supplier
@FunctionalInterface public interface Supplier<T> {T get(); }
无参有返回值的接口。
使用可以省去定义一个外部函数式接口。
定义一个方法来试一下,比如求个数组中最小值。
/*** @author Lenovo*/
public class SupplierTest {public static void main(String[] args) {int[] arr = {1,4,6,2,454,23423,12,35};fun(() -> {Arrays.sort(arr);return arr[0];});}private static void fun(Supplier<Integer> supplier){Integer min = supplier.get();System.out.println("supplier接口函数--数组中的最小值:" + min);}
}
输出结果
supplier接口函数--数组中的最小值:1
Consumer
@FunctionalInterface public interface Consumer<T> {void accept(T t);}
有参无返回值的接口。
Supplier接口用来产生数据,而Consumer是用来消耗数据的,对数据进行操作。
让我们来做个小实验,把字符串转为大写。
/*** @author Lenovo*/
public class ConsumerTest {public static void main(String[] args) {fun(s -> {String s1 = s.toUpperCase(Locale.ROOT);System.out.println(s + "-->大写转化-->" + s1);});}private static void fun(Consumer<String> consumer){String str = "hello";consumer.accept(str);}
}
输出结果
hello-->大写转化-->HELLO
接下来介绍一下默认方法andThen,andThen可以控制两个参数的前后执行顺序。
default Consumer<T> andThen(Consumer<? super T> after) { Objects.requireNonNull(after);
return (T t) -> {
accept(t);
after.accept(t);
};
}
/*** @author Lenovo*/
public class ConsumerTest {public static void main(String[] args) {fun(consumer -> {System.out.println(consumer + "-->大写转化-->" + consumer.toUpperCase());},consumer2 -> {System.out.println(consumer2 + "-->小写转化-->" + consumer2.toLowerCase());});}private static void fun(Consumer<String> consumer,Consumer<String> consumer2){String str = "Hello World";consumer.accept(str);consumer2.accept(str);System.out.println("--------");consumer.andThen(consumer2).accept(str);System.out.println("---------");consumer2.andThen(consumer).accept(str);}
}
输出结果
Hello World-->大写转化-->HELLO WORLD
Hello World-->小写转化-->hello world
--------
Hello World-->大写转化-->HELLO WORLD
Hello World-->小写转化-->hello world
---------
Hello World-->小写转化-->hello world
Hello World-->大写转化-->HELLO WORLD
Function
@FunctionalInterface public interface Function<T, R> {R apply(T t);default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {Objects.requireNonNull(before);return (V v) -> apply(before.apply(v));}default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {Objects.requireNonNull(after);return (T t) -> after.apply(apply(t));}static <T> Function<T, T> identity() {return t -> t;} }
有参有返回值的接口。
R apply(T t);
输入一个T类型,返回一个R类型,做个测试
String转Integer
/*** @author Lenovo*/
public class FunctionTest {public static void main(String[] args) {test(Integer::parseInt);}private static void test(Function<String,Integer> function){Integer apply = function.apply("123");System.out.println("apply==" + apply);}}
输出结果
apply==123
默认方法andThen
default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) { Objects.requireNonNull(after);
return (T t) -> after.apply(apply(t));
}
这个方法和上面的同名方法差不多,就不过多介绍了,compose 刚好顺序和它相反
Predicate
@FunctionalInterface public interface Predicate<T> {boolean test(T t);default Predicate<T> and(Predicate<? super T> other) {Objects.requireNonNull(other);return (t) -> test(t) && other.test(t);}default Predicate<T> negate() {return (t) -> !test(t);}default Predicate<T> or(Predicate<? super T> other) {Objects.requireNonNull(other);return (t) -> test(t) || other.test(t);}static <T> Predicate<T> isEqual(Object targetRef) {return (null == targetRef)? Objects::isNull: object -> targetRef.equals(object);} }
boolean test(T t);
第一个为有参,返回Boolea类型。
/*** @author Lenovo*/
public class PredicateTest {public static void main(String[] args) {test(s -> s.length()>3);}private static void test(Predicate<String> predicate){boolean b = predicate.test("hello");System.out.println("b:" + b);}}
返回结果:true
做个多方法实验
/*** @author Lenovo*/
public class PredicateTest {public static void main(String[] args) {test(p1 -> p1.contains("h"),p2 -> p2.contains("w"));}private static void test(Predicate<String> p1, Predicate<String> p2) {// p1 和 p2 条件同时满足boolean b1 = p1.and(p2).test("hello");// p1 和 p2 满足一个条件boolean b2 = p1.or(p2).test("hello");// p1 的条件否定boolean b3 = p1.negate().test("hello");System.out.println(b1); // falseSystem.out.println(b2); // trueSystem.out.println(b3); // false}}
方法引用与构造器引用
Stream API
1.集合处理数据的弊端
复杂麻烦,反复循环
public class Demo {public static void main(String[] args) {List<String> list = Arrays.asList("张三", "李四", "张三丰", "牛逼啊");//获取姓张的信息List<String> list1 = new ArrayList<>();list.forEach(s -> {if (s.startsWith("张")){list1.add(s);}});//获取名称长度为3的用户List<String> list2 = new ArrayList<>();list.forEach(s -> {if (s.length()==3){list2.add(s);}});//输出所有的用户信息list.forEach(System.out::println);}
}
使用Stream解决以上繁琐
public class Demo1 {public static void main(String[] args) {List<String> list = Arrays.asList("张三", "李四", "张三丰", "牛逼啊");//获取姓张的信息//获取名称长度为3的用户//输出所有的用户信息list.stream().filter(s -> s.startsWith("张")).filter(s -> s.length()==3).forEach(System.out::print);}
}
核心思想
不存储数据,不是一种数据结构。
Stream的两种获取方式
根据Collection获取
java.util.Collection的Stream
public class Demo2 {public static void main(String[] args) {List<String> list = new ArrayList<>();list.stream();Set<String> set = new HashSet<>();set.stream();Vector<String> strings = new Vector<>();strings.stream();}}
map没有直接获取Stream的方法,可以这样:
public class Demo3 {public static void main(String[] args) {Map<String,Object> map = new HashMap<>(); // 通过key获取Stream<String> stream = map.keySet().stream();// 通过值获取Stream<Object> stream1 = map.values().stream();// 通过entry获取Stream<Map.Entry<String, Object>> stream2 = map.entrySet().stream();}
}
通过Stream的of方法获取
在实际开发中我们会操作到数组,由于数组对象不可以直接转化为Steam流,所以
public class Demo4 {public static void main(String[] args) {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,5,6};Stream<Integer> arr22 = Stream.of(arr2);// 基本类型不能直接转化int [] arr3 = {1,3,3,53,6};Stream<int[]> arr31 = Stream.of(arr3);arr31.forEach(System.out::println);}
}
Stream常用方法介绍
注意事项
1.Stream只能操作一次。
2.Stream方法返回的是新的流
3.Stream不调用终结方法(Count、foreach etc..),中间的操作不会执行
forEach
遍历流中的数据
Integer[] arr2 = {1,2,3,4,5,6};
Stream.of(arr2).forEach(System.out::println);
count
Integer[] arr2 = {1,2,3,4,5,6};
System.out.println(Stream.of(arr2).count());
filter
List<String> list = Arrays.asList("张三", "李四", "张三丰", "牛逼啊");//获取姓张的信息//获取名称长度为3的用户//输出所有的用户信息list.stream().filter(s -> s.startsWith("张")).filter(s -> s.length()==3).forEach(System.out::print);
limit
截取limit长度的数据,其他不要
list.stream().limit(3).forEach(System.out::print);
输出前三个
skip
跳过几个元素
list.stream().skip(3).forEach(System.out::print);
map
转化元素
Stream.of("1","2","3","4","5","6")
// .map(s -> Integer.valueOf(s)).map(Integer::valueOf).forEach(System.out::print); //123456
sorted
排序用
Stream.of("1","6","9","4","5","6")
// .map(s -> Integer.valueOf(s)).map(Integer::valueOf).sorted().forEach(System.out::print);// 145669
.sorted((o1, o2) -> o2-o1)
distinct
去掉重复数据,对象集合也可以使用。
.distinct()
match
判断 第一个false 第二个true
List<Employee> list2 = Arrays.asList(new Employee(1, "Alex", 1000),new Employee(2, "Michael", 2000),new Employee(3, "Jack", 1500),new Employee(4, "Owen", 1500),new Employee(5, "Denny", 2000));boolean b = list2.stream().allMatch(employee -> employee.getMoney() > 1000);System.out.println(b);boolean b1 = list2.stream().anyMatch(employee -> employee.getMoney() > 1000);System.out.println(b1);
find
findAny返回任何一个元素,在串行流中返回第一个元素,在并行流中返回处理最快那个线程的元素。
Optional<Employee> any = list2.stream().findAny();System.out.println(any);
findFirst()返回第一个元素,在串行流和并行流中均返回第一个元素。
Optional<Employee> any = list2.stream().findFirst();System.out.println(any);
max和min
最大和最小元素
Optional<Employee> max = list2.stream().max(Comparator.comparingDouble(Employee::getMoney));System.out.println(max);
reduce
Integer sum = Stream.of(4,5,6,3,1)// identity 默认值// 第一次默认值会赋值给x// 之后每次会将 上一次的操作结果赋值给x y就是每次从数据中获取的元素.reduce(0,(x,y)->{System.out.println("x="+x + "y=" +y);return x +y;});System.out.println(sum);System.out.println("----------------");Integer max = Stream.of(4,5,6,4,3).reduce(0,(x,y)->{return x>y ? x:y;});System.out.println(max);
输出
x=0y=4
x=4y=5
x=9y=6
x=15y=3
x=18y=1
19
----------------
6
System.out.println(Stream.iterate(0, x -> x + 1).limit(10).reduce(Integer::sum).orElse(0)); // 45
System.out.println(Stream.iterate(0, x -> x + 1).limit(10).reduce(1000, Integer::sum)); // 1045
System.out.println(Stream.iterate(0, x -> x + 1).limit(10).reduce(1000, Integer::sum, Integer::max)); // 1045
map和reduce的组合
List<Employee> list2 = Arrays.asList(new Employee(1, "Alex", 1000),new Employee(2, "Michael", 2000),new Employee(3, "Jack", 1500),new Employee(4, "Owen", 1500),new Employee(5, "Denny", 2000));// 求所有余额的和Integer reduce = list2.stream().map(Employee::getMoney).reduce(0, Integer::sum);System.out.println(reduce);// 求所有余额的最大值Integer reduce2 = list2.stream().map(Employee::getMoney).reduce(0, Integer::max);System.out.println(reduce2);// 统计字符a出现的次数Integer count = Stream.of("a","b","a","c","a","e").map(ch->"a".equals(ch) ? 1:0).reduce(0,Integer::sum);System.out.println(count); // 3
mapToInt(mapToDoub,mapToLong)
IntStream intStream = Stream.of(arr).mapToInt(Integer::intValue); System.out.println(intStream);
concat
拼接作用 合成一个流
public static void main(String[] args) {System.out.println("输出结果");List<String> list1 = Arrays.asList("迪丽热巴","宋远桥","陈道明","陈美嘉","陆子桥","张晓明");List<String> list2 = Arrays.asList("杨颖","张三","宋钟基","卢布","刘备","曹贼","刘关张");Stream<String> stream1 = list1.stream().filter(s -> s.length()==3);Stream<String> stream2 = list2.stream().filter(s -> s.startsWith("刘"));Stream.concat(stream1,stream2).map(Employee::new).forEach(System.out::println);}
输出结果
Employee(id=null, name=宋远桥, money=null)
Employee(id=null, name=陈道明, money=null)
Employee(id=null, name=陈美嘉, money=null)
Employee(id=null, name=陆子桥, money=null)
Employee(id=null, name=张晓明, money=null)
Employee(id=null, name=刘备, money=null)
Employee(id=null, name=刘关张, money=null)
综合案例
public class Demo1 {public static void main(String[] args) {List<String> list1 = Arrays.asList("迪丽热巴","宋远桥","陈道明","陈美嘉","陆子桥","张晓明");List<String> list2 = Arrays.asList("杨颖","张三","宋钟基","卢布","刘备","曹贼","刘关张");Stream<String> stream1 = list1.stream().filter(s -> s.length()==3);Stream<String> stream2 = list2.stream().filter(s -> s.startsWith("刘"));Stream.concat(stream1,stream2).map(Employee::new).forEach(System.out::println);}}
@NoArgsConstructor
@AllArgsConstructor
@Data
class Employee {private Integer id;private String name;private Integer money;public Employee(String name) {this.name = name;}
}
输出结果
Employee(id=null, name=宋远桥, money=null)
Employee(id=null, name=陈道明, money=null)
Employee(id=null, name=陈美嘉, money=null)
Employee(id=null, name=陆子桥, money=null)
Employee(id=null, name=张晓明, money=null)
Employee(id=null, name=刘备, money=null)
Employee(id=null, name=刘关张, money=null)
结果收集
结果收集到集合
@Testpublic void test01(){// 收集到列表中List<String> collect = Stream.of("aa", "bb", "cc","aa").collect(Collectors.toList());System.out.println(collect);// 收集到集合中Set<String> set = Stream.of("aa", "bb", "cc", "aa").collect(Collectors.toSet());System.out.println(set);// 指定要获取的类具体实现 比如ArrayList HashSetArrayList<String> arrayList = Stream.of("aa", "bb", "cc", "aa").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);}
输出结果:
[aa, bb, cc, aa]
[aa, bb, cc]
[aa, bb, cc, aa]
[aa, bb, cc]
结果收集到数组
/*** Stream 结果收集到数组中*/@Testpublic void test02(){// 返回Object类型数组Object[] objects = Stream.of("aa", "bb", "cc", "aa").toArray();System.out.println(Arrays.toString(objects));String[] strings = Stream.of("aa", "bb", "cc", "aa").toArray(String[]::new);System.out.println(Arrays.toString(strings));}
对流中的数据做聚合计算(最大值、求和、平均值等)
/*** Stream 流做聚合计算*/@Testpublic void test03() {// 获取年龄最大值Optional<Employee> max = Stream.of(new Employee(1, "王聪祥", 15),new Employee(2, "陈晓明", 23),new Employee(3, "关羽", 28),new Employee(4, "张飞", 19),new Employee(5, "诸葛亮", 43)).max(Comparator.comparingInt(Employee::getMoney));System.out.println("最大年龄:" + max.get());Optional<Employee> min = Stream.of(new Employee(1, "王聪祥", 15),new Employee(2, "陈晓明", 23),new Employee(3, "关羽", 28),new Employee(4, "张飞", 19),new Employee(5, "诸葛亮", 43)).min(Comparator.comparingInt(Employee::getMoney));System.out.println("最小年龄:" + min);Integer collect = Stream.of(new Employee(1, "王聪祥", 15),new Employee(2, "陈晓明", 23),new Employee(3, "关羽", 28),new Employee(4, "张飞", 19),new Employee(5, "诸葛亮", 43)).mapToInt(Employee::getMoney).sum();System.out.println("总和:" + collect);Double collect1 = Stream.of(new Employee(1, "王聪祥", 15),new Employee(2, "陈晓明", 23),new Employee(3, "关羽", 28),new Employee(4, "张飞", 19),new Employee(5, "诸葛亮", 43)).collect(Collectors.averagingInt(Employee::getMoney));System.out.println("平均值:" + collect1);long count = Stream.of(new Employee(1, "王聪祥", 15),new Employee(2, "陈晓明", 23),new Employee(3, "关羽", 28),new Employee(4, "张飞", 19),new Employee(5, "诸葛亮", 43)).count();System.out.println("统计数量: " + count);}
输出结果:
最大年龄:Employee(id=5, name=诸葛亮, money=43)
最小年龄:Optional[Employee(id=1, name=王聪祥, money=15)]
总和:128
平均值:25.6
统计数量: 5
分组计算
@Testpublic void test04(){// 根据姓名进行分组Map<String, List<Employee>> ma = Stream.of(new Employee(1, "王聪祥", 1235334),new Employee(2, "诸葛亮", 1535),new Employee(3, "关羽", 2438),new Employee(4, "王聪祥", 5000),new Employee(5, "诸葛亮", 43)).collect(Collectors.groupingBy(Employee::getName));ma.forEach((s, employees) -> System.out.println("key=" + s + "\tvalues=" + employees));}
输出结果:
key=关羽 values=[Employee(id=3, name=关羽, money=2438)]
key=诸葛亮 values=[Employee(id=2, name=诸葛亮, money=1535), Employee(id=5, name=诸葛亮, money=43)]
key=王聪祥 values=[Employee(id=1, name=王聪祥, money=1235334), Employee(id=4, name=王聪祥, money=5000)]
@Testpublic void test05(){// 先根据姓名,再根据余额(小康或者穷逼)进行分组Map<String, Map<String, List<Employee>>> collect = Stream.of(new Employee(1, "王聪祥", 1235334),new Employee(2, "诸葛亮", 1535),new Employee(3, "关羽", 2438),new Employee(4, "王聪祥", 5000),new Employee(5, "诸葛亮", 43)).collect(Collectors.groupingBy(Employee::getName,Collectors.groupingBy(e -> e.getMoney() >= 3000 ? "有钱" : "穷逼")));collect.forEach((s, integerListMap) -> System.out.println("key=" + s + "\tvalues=" + integerListMap));}
输出结果:
key=关羽 values={穷逼=[Employee(id=3, name=关羽, money=2438)]}
key=诸葛亮 values={穷逼=[Employee(id=2, name=诸葛亮, money=1535), Employee(id=5, name=诸葛亮, money=43)]}
key=王聪祥 values={有钱=[Employee(id=1, name=王聪祥, money=1235334), Employee(id=4, name=王聪祥, money=5000)]}
对流中的数据做分区操作
对流中的数据做两个划分,条件达成为true,不达成则是false
Stream.of(new Employee(1, "王聪祥", 1235334),new Employee(2, "诸葛亮", 1535),new Employee(3, "关羽", 2438),new Employee(4, "王聪祥", 5000),new Employee(5, "诸葛亮", 43)).collect(Collectors.partitioningBy(employee -> employee.getMoney()>3000)).forEach((s, integerListMap) -> System.out.println("key=" + s + "\tvalues=" + integerListMap));
输出结果
key=false values=[Employee(id=2, name=诸葛亮, money=1535), Employee(id=3, name=关羽, money=2438), Employee(id=5, name=诸葛亮, money=43)]
key=true values=[Employee(id=1, name=王聪祥, money=1235334), Employee(id=4, name=王聪祥, money=5000)]
对流中的数据做拼接
String collect = Stream.of(new Employee(1, "王聪祥", 1235334),new Employee(2, "诸葛亮", 1535),new Employee(3, "关羽", 2438),new Employee(4, "王聪祥", 5000),new Employee(5, "诸葛亮", 43)).map(Employee::getName).collect(Collectors.joining());System.out.println(collect);String collect2 = Stream.of(new Employee(1, "王聪祥", 1235334),new Employee(2, "诸葛亮", 1535),new Employee(3, "关羽", 2438),new Employee(4, "王聪祥", 5000),new Employee(5, "诸葛亮", 43)).map(Employee::getName).collect(Collectors.joining("_"));System.out.println(collect2);String collect3 = Stream.of(new Employee(1, "王聪祥", 1235334),new Employee(2, "诸葛亮", 1535),new Employee(3, "关羽", 2438),new Employee(4, "王聪祥", 5000),new Employee(5, "诸葛亮", 43)).map(Employee::getName).collect(Collectors.joining("_", "###", "$$$"));System.out.println(collect3);
输出结果
王聪祥诸葛亮关羽王聪祥诸葛亮
王聪祥_诸葛亮_关羽_王聪祥_诸葛亮
###王聪祥_诸葛亮_关羽_王聪祥_诸葛亮$$$
并行的Stream流
串行流
上面的Stream处理方式都是串行的方式,也都是用主线程来执行
/*** 串行流*/@Testpublic void test01(){Stream.of(1,2,3,4,5,6,7,4,3,2,11).filter(s->{System.out.println(Thread.currentThread()+ "--" + s);return s > 3;}).count();}
输出结果
Thread[main,5,main]--1
Thread[main,5,main]--2
Thread[main,5,main]--3
Thread[main,5,main]--4
Thread[main,5,main]--5
Thread[main,5,main]--6
Thread[main,5,main]--7
Thread[main,5,main]--4
Thread[main,5,main]--3
Thread[main,5,main]--2
Thread[main,5,main]--11
创建并行流
parallelStream 并行流,它通过ForkjoinPool,可以提高多线程任务的速度。
两种方式创建并行流
/*** 两种方式创建并行流*/@Testpublic void test02(){// 通过List 接口 直接获取并行流List<Integer> list = new ArrayList<>();Stream<Integer> integerStream = list.parallelStream();// 通过串行流转为并行流Stream<Integer> integerStream1 = Stream.of(1, 2, 3, 5).parallel();}
并行流操作
/*** 并行流操作*/@Testpublic void test03(){Stream.of(1,3,4,5,6,3,9,12,11).parallel().filter(s->{System.out.println(Thread.currentThread() + "---" + s);return s>2;}).count();}
输出结果
Thread[ForkJoinPool.commonPool-worker-2,5,main]---12
Thread[main,5,main]---3
Thread[ForkJoinPool.commonPool-worker-6,5,main]---1
Thread[ForkJoinPool.commonPool-worker-1,5,main]---4
Thread[ForkJoinPool.commonPool-worker-4,5,main]---9
Thread[ForkJoinPool.commonPool-worker-7,5,main]---11
Thread[ForkJoinPool.commonPool-worker-3,5,main]---3
Thread[ForkJoinPool.commonPool-worker-5,5,main]---5
Thread[ForkJoinPool.commonPool-worker-2,5,main]---6
并行流比较串行流
通过for循环、串行和并行做比较。
private static long times = 500000000;private long start;@Beforepublic void before(){start = System.currentTimeMillis();}@Afterpublic void end(){long end = System.currentTimeMillis();System.out.println("消耗时间==" + (end-start));}/*** 普通for 循环 消耗时间: 281*/@Testpublic void test01(){System.out.println("普通for循环");long res = 0;for (int i = 0; i < times; i++) {res += i;}}/*** 串行流 消耗时间:313*/@Testpublic void test02(){System.out.println("串行流");LongStream.rangeClosed(0,times).reduce(0,Long::sum);}/*** 并行流 消耗时间:99*/@Testpublic void test03(){System.out.println("并行流");LongStream.rangeClosed(0,times).parallel().reduce(0,Long::sum);}
线程安全问题
多线程状态下肯定会有线程安全问题,如下:
/*** 并行流中的数据安全问题*/@Testpublic void test04(){List<Integer> list = new ArrayList<>();for (int i = 0; i < 1000; i++) {list.add(i);}System.out.println(list.size());List<Integer> list1 = new ArrayList<>();list.stream().parallel()// .forEach(s-> New.add(s));.forEach(list1::add);System.out.println(list1.size());}
java.lang.ArrayIndexOutOfBoundsException
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
....
at java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:157)
解决办法如下:
/*** 第一种方法:* 加同步锁*/@Testpublic void test05(){List<Integer> list = new ArrayList<>();for (int i = 0; i < 1000; i++) {list.add(i);}System.out.println(list.size());List<Integer> list1 = new ArrayList<>();Object obj = new Object();list.stream().parallel()// .forEach(s-> New.add(s));.forEach(s->{synchronized (obj){list1.add(s);}});System.out.println(list1.size());}/*** 第2种方法:* 使用线程安全的容器*/@Testpublic void test06(){List<Integer> list = new ArrayList<>();for (int i = 0; i < 1000; i++) {list.add(i);}System.out.println(list.size());Vector<Integer> list1 = new Vector<>();list.stream().parallel()// .forEach(s-> New.add(s));.forEach(list1::add);System.out.println(list1.size());}/*** 第3种方法:* 使用Stream的 toArray或 collect 方法来操作*/@Testpublic void test07(){List<Integer> list = new ArrayList<>();for (int i = 0; i < 1000; i++) {list.add(i);}System.out.println(list.size());List<Integer> list1 = list.stream().parallel().collect(Collectors.toList());System.out.println(list1.size());Integer[] list2 = list.stream().parallel().toArray(Integer[]::new);System.out.println(list2.length);}
Fork/Join框架
parallerStream 使用的是Fork/Join框架,Fork/Join从jdk7引入,Fork/Join框架可以将一个大人物拆分为多个小任务来执行,Fork/Join主要包括三个模块:
1.线程池:ForkJoinPool
2.任务对象:ForkJoinTask
3.执行的线程:ForkJoinWorkerThread
Optional类
Optional类主要解决空指针问题
创建Optional
// 第一张方式 通过of方法 但是不支持nullOptional<String> op1 = Optional.of("张三");
// Optional<String> op2 = Optional.of(null);// 第二种方式 通过ofNullable方法 支持nullOptional<String> op3 = Optional.ofNullable("李四");Optional<String> op4 = Optional.ofNullable(null);// 第三种方式 通过empty方法直接创建一个空的Optional对象Optional<String> op5 = Optional.empty();
基本方法介绍和使用
/*** Optional 中常用方法介绍* get(): 如果Optional有值则返回,否则抛出NoSuchElementException异常* ps:get() 通常和isPresent()方法一起使用* isPresent(): 判断是否包含值,包含值返回true,不包含返回false* orElse(value):如果调用对象包含值,就返回值,否则返回value* orElseGet(lambda):和orElse差不多,但是orElseGet(lambda)只有对象为空时才创建对象*/@Testpublic void test01(){Optional<String> op1 = Optional.of("张三");Optional<String> op2 = Optional.empty();if (op1.isPresent()){System.out.println("当前值为:" + op1);}else {System.out.println("当前对象没有值呢");}if (op2.isPresent()){System.out.println("当前值为:" + op2);}else {System.out.println("当前对象没有值呢");}System.out.println(op1.orElse("刘明"));System.out.println(op2.orElse("李四"));String s1= op2.orElseGet(() -> "hello");System.out.println(s1);}
高级用法
首先可以用ifOptional
@Testpublic void test01(){Optional<String> op1 = Optional.of("张三");Optional<String> op2 = Optional.empty();// 如果存在值 就做什么op1.ifPresent(s-> System.out.println("有值:" + s));op1.ifPresent(System.out::println);}
接下来介绍一下判断对象空值方法
获取Employee对象名字并返回大写形式
public String getName(Employee employee){if (employee != null){String name = employee.getName();if (name != null){return name.toUpperCase();}else {return null;}}else {return null;}}
如果用Optional方式:
public String getName(Optional<Employee> employee) {if (employee.isPresent()) {String msg = employee.map(Employee::getName).orElse("空值");return msg;}return null;}