Java8新特征

更新于 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() {
            @Override
            public 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() {
            @Override
            public 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>() {
            @Override
            public 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 {
    @Override
    public void test1() {

    }

    @Override
    public 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 {
    @Override
    public void test1() {

    }

    @Override
    public void test2() {

    }

    @Override
    public String test3() {
        System.out.println("B 实现类中重写了默认方法。。");
        return "ok...";
    }
}

class C implements A {
    @Override
    public void test1() {

    }

    @Override
    public 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); // false
        System.out.println(b2); // true
        System.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)

结果收集

结果收集到集合

    @Test
    public 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 HashSet
        ArrayList<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 结果收集到数组中
     */
    @Test
    public 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 流做聚合计算
     */
    @Test
    public 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

分组计算

    @Test
    public 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)]
    @Test
    public 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处理方式都是串行的方式,也都是用主线程来执行

    /**
     * 串行流
     */
    @Test
    public 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,可以提高多线程任务的速度。

两种方式创建并行流

    /**
     * 两种方式创建并行流
     */
    @Test
    public void test02(){
       // 通过List 接口 直接获取并行流
        List<Integer> list = new ArrayList<>();
        Stream<Integer> integerStream = list.parallelStream();
        // 通过串行流转为并行流
        Stream<Integer> integerStream1 = Stream.of(1, 2, 3, 5)
                .parallel();
    }

并行流操作

    /**
     * 并行流操作
     */
    @Test
    public 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;

    @Before
    public void before(){
        start = System.currentTimeMillis();
    }

    @After
    public void end(){
        long end = System.currentTimeMillis();
        System.out.println("消耗时间==" + (end-start));
    }

    /**
     * 普通for 循环 消耗时间: 281
     */
    @Test
    public void test01(){
        System.out.println("普通for循环");
        long res = 0;
        for (int i = 0; i < times; i++) {
            res += i;
        }
    }

    /**
     * 串行流 消耗时间:313
     */
    @Test
    public void test02(){
        System.out.println("串行流");
        LongStream.rangeClosed(0,times)
                .reduce(0,Long::sum);
    }

    /**
     * 并行流 消耗时间:99
     */
    @Test
    public void test03(){
        System.out.println("并行流");
        LongStream.rangeClosed(0,times)
                .parallel()
                .reduce(0,Long::sum);
    }





线程安全问题

多线程状态下肯定会有线程安全问题,如下:

    /**
     * 并行流中的数据安全问题
     */
    @Test
    public 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)

解决办法如下:

    /**
     * 第一种方法:
     * 加同步锁
     */
    @Test
    public 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种方法:
     * 使用线程安全的容器
     */
    @Test
    public 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 方法来操作
     */
    @Test
    public 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方法 但是不支持null
        Optional<String> op1 = Optional.of("张三");
//        Optional<String> op2 = Optional.of(null);
        // 第二种方式 通过ofNullable方法 支持null
        Optional<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)只有对象为空时才创建对象
     */
    @Test
    public 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

    @Test
    public 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;
    }

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值