Java8新特征

news/2024/11/25 10:49:09/

更新于 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;}


http://www.ppmy.cn/news/415856.html

相关文章

LuatOS-Air AT应用指南--阿里云

1 概述&#xff1a; 物联网平台提供安全可靠的设备连接通信能力&#xff0c;支持设备数据采集上云&#xff0c;规则引擎流转数据和云端数据下发设备端。此外&#xff0c;也提供方便快捷的设备管理能力&#xff0c;支持物模型定义&#xff0c;数据结构化存储&#xff0c;和远程调…

35:考虑virtual函数以外的其他选择

假设你正在写一个视频游戏软件&#xff0c;你打算为游戏内的人物设计一个继承体系&#xff0c;剧中人物被伤害或因其他因素而降低健康状态的情况并不罕见。你因此决定提供一个成员函数healthValue&#xff0c;它会返回一个整数&#xff0c;表示人物的健康程度。 由于不同的人物…

Redis中常见的一些问题

缓存穿透问题 什么是缓存穿透&#xff1f; 例如当我们根据id查询一个数据的时候&#xff0c;但是这个数据本身不存在或者已经被删除之后&#xff0c;缓存中不存在&#xff0c;就会去查询数据库&#xff0c;但是不存在的数据不会缓存到数据库中&#xff0c;那么一旦大量的这个请…

力扣 104. 二叉树的最大深度

题目来源&#xff1a;https://leetcode.cn/problems/maximum-depth-of-binary-tree/description/ C题解&#xff1a;层序遍历&#xff0c; 每层说明深度加一。 /*** Definition for a binary tree node.* struct TreeNode {* int val;* TreeNode *left;* TreeNode…

❤️手把手教你装微软官方工具❤️

微软官方工具装U启方法 微软工具链接&#xff1a;https://www.microsoft.com/zh-cn/software-download/windows 点击立即下载工具&#xff0c;下载到本地 点击接受 可以直接为本机装系统&#xff0c;也可以装U盘里边 选择不同版本的系统和语言&#xff0c;如果不需要&#xff0…

腾讯android一键root工具,腾讯一键Root工具pc版下载_腾讯一键Root工具pc版官方下载-太平洋下载中心...

常见问答&#xff1a; 1、腾讯一键root工具如何卸载&#xff1f; 答&#xff1a;1、一键root时要自动安装授权管理&#xff0c;如果不ROOT了&#xff0c;想卸载“授权管理”的话,直接卸载会提示“系统软件无法卸载”。不用着急&#xff0c;接着看。 2、首先打开“授权管理”软件…

Python爬虫练习(一)

文章目录 前言代码1.引入库2.定义Pokemon类3.打印进度条4.伪造浏览器5.爬取网页6.获取宝可梦列表7.查找属性和分类8.爬取与写入9.主程序 总结 前言 使用requests库爬取宝可梦列表&#xff0c;使用BeautifulSoup库解析并查找对应宝可梦的属性和分类 代码 1.引入库 import requ…

自学python第十天之:从0完成一个宝可梦数据分析实战

数据集&#xff1a;包含着从第一代到第七代共801只宝可梦的数据集。 环境&#xff1a;tf2.0 anaconda 1.数据集下载 # 数据集下载 !wget -O pokemon_data.csv https://pai-public-data.oss-cn-beijing.aliyuncs.com/pokemon/pokemon.csv 2.然后我们import我们最常用的三大件…