Java8新特性Lambda表达式详细

news2024/11/28 6:27:18

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
Comparator:此接口中只包含一个方法

int compare(T A,T B)
如果A>B,返回正数
如果A=B,返回0
入伙A<B,返回负数

Lambda表达式

函数式重点:只需要关注参数列表和方法体
看参数ctrl+p
需求分析
我们在创建线程并启动时可以使用匿名内部类的写法:

案例1

new Thread(new Runnable() {
    @Override
    public void run() {
        System.out.println("你知道吗 我比你想象的 更想在你身边");
    }
}).start();

可以使用Lambda的格式对其进行修改。修改后如下:

new Thread(()->{
    System.out.println("你知道吗 我比你想象的 更想在你身边");
}).start();

代码分析:

  1. Thread类需要一个Runnable接口作为参数,其中的抽象方法run方法是用来指定线程任务内容的
    核心
  2. 为了指定run方法体,不得不需要Runnable的实现类
  3. 为了省去定义一个Runnable 的实现类,不得不使用匿名内部类
  4. 必须覆盖重写抽象的run方法,所有的方法名称,方法参数,方法返回值不得不都重写一遍,而且
    不能出错,
  5. 而实际上,我们只在乎方法体中的代码

Lambda表达式的优点:简化了匿名内部类的使用,语法更加简单。
匿名内部类语法冗余,体验了Lambda表达式后,发现Lambda表达式是简化匿名内部类的一种方式。

Lambda的语法规则

Lambda省去了面向对象的条条框框Lambda的标准格式由3个部分组成:

(参数类型 参数名称) -> {
	代码体;
}

格式说明:
(参数类型 参数名称):参数列表
{代码体;} :方法体
-> : 箭头,分割参数列表和方法体

看不懂的时候用alt加回车转换成匿名内部类
在这里插入图片描述

案例二

现有方法定义如下,其中IntBinaryOperator是一个接口。先使用匿名内部类的写法调用该方法。

    public static int calculateNum(IntBinaryOperator operator){
        int a = 10;
        int b = 20;
        return operator.applyAsInt(a, b);
    }

    public static void main(String[] args) {
        int i = calculateNum(new IntBinaryOperator() {
            @Override
            public int applyAsInt(int left, int right) {
                return left + right;
            }
        });
        System.out.println(i);
    }

Lambda写法:

    public static void main(String[] args) {
        int i = calculateNum((int left, int right)->{
            return left + right;
        });
        System.out.println(i);
    }
    可以改造成
       int i = calculateNum((left, right) -> left + right);
       System.out.println(i);

案例三
现有方法定义如下,其中IntPredicate是一个接口。先使用匿名内部类的写法调用该方法。

    public static void printNum(IntPredicate predicate){
        int[] arr = {1,2,3,4,5,6,7,8,9,10};
        for (int i : arr) {
            if(predicate.test(i)){   i是便利到的元素传入到predicate.test中做为参数传进去
                System.out.println(i);
            }
        }
    }
    public static void main(String[] args) {
        printNum(new IntPredicate() {
            @Override
            public boolean test(int value) {
                return value%2==0;
            }
        });
    }

Lambda写法:

    public static void main(String[] args) {
        printNum((int value)-> {
            return value%2==0;
        });
    }
    public static void printNum(IntPredicate predicate){
        int[] arr = {1,2,3,4,5,6,7,8,9,10};
        for (int i : arr) {
            if(predicate.test(i)){
                System.out.println(i);
            }
        }
    }
    printNum(value-> value%2==0);

例四:

泛型现有方法定义如下,其中Function是一个接口。先使用匿名内部类的写法调用该方法。

    public static <R> R typeConver(Function<String,R> function){
        String str = "1235";
        R result = function.apply(str);
        return result;
    }
    public static void main(String[] args) {
        Integer result = typeConver(new Function<String, Integer>() {      传入的是String ,返回的是Integer
            @Override
            public Integer apply(String s) {
                return Integer.valueOf(s);
            }
        });
        System.out.println(result);
    }

Lambda写法:

        Integer result = typeConver((String s)->{
            return Integer.valueOf(s);
        });
        System.out.println(result);
        Integer result = typeConver(s -> Integer.valueOf(s));

如果传的是String,返回的也是String

    public static <R> R typeConver(Function<String,R> function){
        String str = "1235";
        R result = function.apply(str);
        return result;
    }
    public static void main(String[] args) {
        String result = typeConver(new Function<String, String>() {      传入的是String ,返回的是Integer
            @Override
            public String apply(String s) {
                return s+"哈喽";
            }
        });
        System.out.println(result);
    }

Lambda写法:

        String result = typeConver((String s)->{
            return s+"哈喽";;
        });
        System.out.println(result);

例五:

现有方法定义如下,其中IntConsumer是一个接口。先使用匿名内部类的写法调用该方法。

    public static void foreachArr(IntConsumer consumer){
        int[] arr = {1,2,3,4,5,6,7,8,9,10};
        for (int i : arr) {
            consumer.accept(i);
        }
    }
    public static void main(String[] args) {
        foreachArr(new IntConsumer() {
            @Override
            public void accept(int value) {
                System.out.println(value);
            }
        });
    }

Lambda写法:

    public static void main(String[] args) {
        foreachArr((int value)->{
            System.out.println(value);
        });
    }

2.4 省略规则

  • 参数类型可以省略
  • 方法体只有一句代码时大括号return和唯一一句代码的分号可以省略
  • 方法只有一个参数时小括号可以省略
  • 以上这些规则都记不住也可以省略不记

不会就使用alt+回车提示,看见lambda的,优化和还原回来,光标点前面的方法还原

看一个例子怎么优化成这个的
在这里插入图片描述
现在是匿名内部类的写法
在这里插入图片描述

去除这些变成最优的写法

3、Stream流

3.1 概述

​ Java8的Stream使用的是函数式编程模式,如同它的名字一样,它可以被用来对集合或数组进行链状流式的操作。可以更方便的让我们对集合或数组操作。

3.2 案例数据准备

    <dependencies>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.16</version>
        </dependency>
    </dependencies>
@Data
@NoArgsConstructor//空参构造
@AllArgsConstructor//有参构造
@EqualsAndHashCode//用于后期的去重使用
public class Author {
    //id
    private Long id;
    //姓名
    private String name;
    //年龄
    private Integer age;
    //简介
    private String intro;
    //作品
    private List<Book> books;
}
@Data
@AllArgsConstructor//有参构造
@NoArgsConstructor//空参构造
@EqualsAndHashCode//用于后期的去重使用
public class Book {
    //id
    private Long id;
    //书名
    private String name;

    //分类
    private String category;

    //评分
    private Integer score;

    //简介
    private String intro;

}
    private static List<Author> getAuthors() {
        //数据初始化
        Author author = new Author(1L,"蒙多",33,"一个从菜刀中明悟哲理的祖安人",null);
        Author author2 = new Author(2L,"亚拉索",15,"狂风也追逐不上他的思考速度",null);
        Author author3 = new Author(3L,"易",14,"是这个世界在限制他的思维",null);
        Author author4 = new Author(3L,"易",14,"是这个世界在限制他的思维",null);

        //书籍列表
        List<Book> books1 = new ArrayList<>();
        List<Book> books2 = new ArrayList<>();
        List<Book> books3 = new ArrayList<>();

        books1.add(new Book(1L,"刀的两侧是光明与黑暗","哲学,爱情",88,"用一把刀划分了爱恨"));
        books1.add(new Book(2L,"一个人不能死在同一把刀下","个人成长,爱情",99,"讲述如何从失败中明悟真理"));

        books2.add(new Book(3L,"那风吹不到的地方","哲学",85,"带你用思维去领略世界的尽头"));
        books2.add(new Book(3L,"那风吹不到的地方","哲学",85,"带你用思维去领略世界的尽头"));
        books2.add(new Book(4L,"吹或不吹","爱情,个人传记",56,"一个哲学家的恋爱观注定很难把他所在的时代理解"));

        books3.add(new Book(5L,"你的剑就是我的剑","爱情",56,"无法想象一个武者能对他的伴侣这么的宽容"));
        books3.add(new Book(6L,"风与剑","个人传记",100,"两个哲学家灵魂和肉体的碰撞会激起怎么样的火花呢?"));
        books3.add(new Book(6L,"风与剑","个人传记",100,"两个哲学家灵魂和肉体的碰撞会激起怎么样的火花呢?"));

        author.setBooks(books1);
        author2.setBooks(books2);
        author3.setBooks(books3);
        author4.setBooks(books3);

        List<Author> authorList = new ArrayList<>(Arrays.asList(author,author2,author3,author4));
        return authorList;
    }

3.3 快速入门

3.3.1 需求

​ 我们可以调用getAuthors方法获取到作家的集合。现在需要打印所有年龄小于18的作家的名字,并且要注意去重。

3.3.2 实现

        //打印所有年龄小于18的作家的名字,并且要注意去重
        List<Author> authors = getAuthors();
        authors.				//选中这里和最后,按+ctrl+alt+回车收起来
                stream()//把集合转换成流
                .distinct()//先去除重复的作家
                .filter(author -> author.getAge()<18)//筛选年龄小于18的
                .forEach(author -> System.out.println(author.getName()));//遍历打印名字

不会写先用匿名内部类的写法,在用快捷键优化alt加回车

    public static void main(String[] args) {
        //打印所有年龄小于18的作家的名字,并且要注意去重
        List<Author> authors = getAuthors();
        authors.
                stream()//把集合转换成流
                .distinct()//先去除重复的作家
                .filter(new Predicate<Author>() {
                    @Override
                    public boolean test(Author author) {
                        return author.getAge()<18;
                    }
                })
                .forEach(new Consumer<Author>() {
                    @Override
                    public void accept(Author author) {
                        System.out.println(author.getName());
                    }
                });
    }

推荐一下IDEA的debugger,打上断点,点底下这个出来,能看到运行的信息
在这里插入图片描述

3.4 常用操作

必须要有的三个步骤,创建流,中间操作,终结操作

3.4.1 创建流

forEach相当于就打印出来

单列集合: 集合对象.stream()

        List<Author> authors = getAuthors();
		Stream<Author> stream = authors.stream();

数组:Arrays.stream(数组) 或者使用Stream.of来创建

        Integer[] arr = {1,2,3,4,5};
        Stream<Integer> stream = Arrays.stream(arr);
        Stream<Integer> stream2 = Stream.of(arr);
        
                stream2.distinct()
                .filter(integer -> integer>2)
                .forEach(integer -> System.out.println(integer));

双列集合:转换成单列集合后再创建

        Map<String,Integer> map = new HashMap<>();
        map.put("蜡笔小新",19);
        map.put("黑子",17);
        map.put("日向翔阳",16);

        Stream<Map.Entry<String, Integer>> stream = map.entrySet().stream();

拆出来用匿名内部类方式

        Set<Map.Entry<String, Integer>> entries = map.entrySet();
        Stream<Map.Entry<String, Integer>> stream = entries.stream();
        stream.filter(new Predicate<Map.Entry<String, Integer>>() {
            @Override
            public boolean test(Map.Entry<String, Integer> entry) {
                return entry.getValue()>16;
            }
        }).forEach(new Consumer<Map.Entry<String, Integer>>() {
            @Override
            public void accept(Map.Entry<String, Integer> entry) {
                System.out.println(entry.getKey()+"==="+entry.getValue());
            }
        });

3.4.2 中间操作

filter

​ 可以对流中的元素进行条件过滤,符合过滤条件的才能继续留在流中。
例如:

​ 打印所有姓名长度大于1的作家的姓名

        List<Author> authors = getAuthors();
        authors.stream()
                .filter(author -> author.getName().length()>1)
                .forEach(author -> System.out.println(author.getName()));
        List<Author> authors = getAuthors();
        authors.stream()
                .filter(new Predicate<Author>() {
                    @Override
                    public boolean test(Author author) {
                        return author.getName().length()>1;
                    }
                }).forEach(new Consumer<Author>() {
                    @Override
                    public void accept(Author author) {
                        System.out.println(author.getName());
                    }
                });
map

​ 可以把对流中的元素进行计算或转换。
map只能将一个对象转换成另外一个对象来作为流中的元素,而flatmap可以把一个对象转换成多个对象作为流中的元素

例如:

​ 打印所有作家的姓名,这是转换操作

        List<Author> authors = getAuthors();

        authors
                .stream()
                .map(author -> author.getName())
                .forEach(name->System.out.println(name));
//        打印所有作家的姓名
        List<Author> authors = getAuthors();

//        authors.stream()
//                .map(author -> author.getName())
//                .forEach(s -> System.out.println(s));

        authors.stream()
                .map(author -> author.getAge())
                .map(age->age+10)
                .forEach(age-> System.out.println(age));

在这里插入图片描述

我们可以对Optional中对象进行处理,然后在返回该对象,因为map()方法中的Function函数式接口要求返回值就是Optional中的对象类型

Optional<Employee> optional = Optional.ofNullable(null);
System.out.println(optional.map(x -> {
    x.setName("小亮");
    x.setAge(17);
    return x;
}).get());

distinct

​ 可以去除流中的重复元素。

例如:

​ 打印所有作家的姓名,并且要求其中不能有重复元素。

        List<Author> authors = getAuthors();
        authors.stream()
                .distinct()
                .forEach(author -> System.out.println(author.getName()));

注意:distinct方法是依赖Object的equals方法来判断是否是相同对象的。所以需要注意重写equals方法。

sorted

​ 可以对流中的元素进行排序。

例如:

​ 对流中的元素按照年龄进行降序排序,并且要求不能有重复的元素。
下面是空参的配置
在这里插入图片描述

        List<Author> authors = getAuthors();
//        对流中的元素按照年龄进行降序排序,并且要求不能有重复的元素。
        authors.stream()
                .distinct()
                .sorted()
                .forEach(author -> System.out.println(author.getAge()));
        List<Author> authors = getAuthors();
//        对流中的元素按照年龄进行降序排序,并且要求不能有重复的元素。
        authors.stream()
                .distinct()
                .sorted((o1, o2) -> o2.getAge()-o1.getAge())
                .forEach(author -> System.out.println(author.getAge()));

注意:如果调用空参的sorted()方法,需要流中的元素是实现了Comparable。

limit

​ 可以设置流的最大长度,超出的部分将被抛弃。

例如:

​ 对流中的元素按照年龄进行降序排序,并且要求不能有重复的元素,然后打印其中年龄最大的两个作家的姓名。

        List<Author> authors = getAuthors();
        authors.stream()
                .distinct()
                .sorted((o1, o2) -> o2.getAge()-o1.getAge())
                .limit(2)
                .forEach(author -> System.out.println(author.getName()));
skip

​ 跳过流中的前n个元素,返回剩下的元素

例如:

​ 打印除了年龄最大的作家外的其他作家,要求不能有重复元素,并且按照年龄降序排序。

//        打印除了年龄最大的作家外的其他作家,要求不能有重复元素,并且按照年龄降序排序。
        List<Author> authors = getAuthors();
        authors.stream()
                .distinct()
                .sorted((o1, o2) -> o2.getAge()-o1.getAge())
                .skip(1)
                .forEach(author -> System.out.println(author.getName()));
flatMap

map只能把一个对象转换成另一个对象来作为流中的元素。而flatMap可以把一个对象转换成多个对象作为流中的元素。
对Optional中的对象进行处理,然后在返回一个Optional对象,返回Optional对象中的对象类型还是原来的类型,比如上面就是Employee类型

例一:

​ 打印所有书籍的名字。要求对重复的元素进行去重。

//        打印所有书籍的名字。要求对重复的元素进行去重。
        List<Author> authors = getAuthors();

        authors.stream()
                .flatMap(author -> author.getBooks().stream())
                .distinct()
                .forEach(book -> System.out.println(book.getName()));

在这里插入图片描述

例二:

​ 打印现有数据的所有分类。要求对分类进行去重。不能出现这种格式:哲学,爱情
字符串分类

//        打印现有数据的所有分类。要求对分类进行去重。不能出现这种格式:哲学,爱情     爱情
        List<Author> authors = getAuthors();
        authors.stream()
                .flatMap(author -> author.getBooks().stream())
                .distinct()
                .flatMap(book -> Arrays.stream(book.getCategory().split(",")))
                .distinct()
                .forEach(category-> System.out.println(category));

在填写默认值,避免出现空指针异常问题上,倒是一把好手,也避免我们写很多的if判空判断

public class Test {
    public static void main(String[] args) {
        System.out.println(getEmployeeName(Optional.ofNullable(null)));
    }

    private static String getEmployeeName(Optional<Boss> boss) {
        String name = boss.orElse(new Boss()).getEmployee().orElse(new Employee(17, "李华")).getName();
        return name;
    }
}

@Data
class Boss {
    private Optional<Employee> employee = Optional.empty();
}

@Data
@NoArgsConstructor
@AllArgsConstructor
class Employee {
    private Integer age;
    private String name;
}

3.4.3 终结操作

forEach

​ 对流中的元素进行遍历操作,我们通过传入的参数去指定对遍历到的元素进行什么具体操作。

例子:

​ 输出所有作家的名字

//        输出所有作家的名字
        List<Author> authors = getAuthors();

        authors.stream()
                .map(author -> author.getName())
                .distinct()
                .forEach(name-> System.out.println(name));
count

​ 可以用来获取当前流中元素的个数。

例子:

​ 打印这些作家的所出书籍的数目,注意删除重复元素。

//        打印这些作家的所出书籍的数目,注意删除重复元素。
        List<Author> authors = getAuthors();

        long count = authors.stream()
                .flatMap(author -> author.getBooks().stream())
                .distinct()
                .count();
        System.out.println(count);
max&min

​ 可以用来或者流中的最值。最大值,最小值

例子:

​ 分别获取这些作家的所出书籍的最高分和最低分并打印。

//        分别获取这些作家的所出书籍的最高分和最低分并打印。
        //Stream<Author>  -> Stream<Book> ->Stream<Integer>  ->求值

        List<Author> authors = getAuthors();
        Optional<Integer> max = authors.stream()
                .flatMap(author -> author.getBooks().stream())
                .map(book -> book.getScore())
                .max((score1, score2) -> score1 - score2);

        Optional<Integer> min = authors.stream()
                .flatMap(author -> author.getBooks().stream())
                .map(book -> book.getScore())
                .min((score1, score2) -> score1 - score2);
        System.out.println(max.get());
        System.out.println(min.get());

在这说明一下为什么不能点了,现在map还是流的操作,max是终结操作
在这里插入图片描述
在这里插入图片描述

collect

​ 把当前流转换成一个集合。
将流转为List,Set,Map集合

Collectors.toList()是将Stream转换成List,List为ArrayList类型
Collectors.toSet()是将Stream转换成Set,Set为HashSet类型
Collectors.toMap()是将Stream转换成Map,Map为HashMap
要转换成指定的集合类型,则可以使用Collectors.toCollection(),如下

    List<Person> listResult = pList.stream().collect(Collectors.toCollection(LinkedList::new));

例子:

​ 获取一个存放所有作者名字的List集合。

//        获取一个存放所有作者名字的List集合。
        List<Author> authors = getAuthors();
        List<String> nameList = authors.stream()
                .map(author -> author.getName())
                .collect(Collectors.toList());
        System.out.println(nameList);
        System.out.println("stream() result :"+nameList .getClass());

获取一个所有书名的Set集合。

//        获取一个所有书名的Set集合。
        List<Author> authors = getAuthors();
        Set<Book> books = authors.stream()
                .flatMap(author -> author.getBooks().stream())
                .collect(Collectors.toSet());

        System.out.println(books);

获取一个Map集合,map的key为作者名,value为List

//        获取一个Map集合,map的key为作者名,value为List<Book>
        List<Author> authors = getAuthors();

        Map<String, List<Book>> map = authors.stream()
                .distinct()
                .collect(Collectors.toMap(author -> author.getName(), author -> author.getBooks()));

        System.out.println(map);

在这里插入图片描述
指定转换成集合类型LinkedHashMap

import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class Main {
   public static class Person {
     int age;
     String name;
     Person (int age,String name) {
         this.age = age;
         this.name = name;
     }
     public int getAge() {
         return age;
     }
     public void setAge(int age) {
         this.age = age;
     }
     public String getName() {
         return name;
     }
     public void setName(String name) {
         this.name = name;
     }

  }
public static void main(String[] args) {

    List<Person> pList = Arrays.asList(new Person(11,"mike"),new Person(22,"lynn"), new Person(33,"john"), new Person(44,"mickey"), new Person(55,"fiona"), new Person(31,"fiona"));
    //(oldValue, newValue) -> newValue是重复key时,使用的值,可以指定
    Map<String, Integer> map = pList.stream().collect(Collectors.toMap(Person::getName, Person::getAge,(oldValue, newValue) -> newValue));
    System.out.println("stream() result :"+map);
    System.out.println("stream() result :"+map.getClass());
    //指定转换成集合类型LinkedHashMap
    Map<String, Integer> map1 = pList.stream().collect(Collectors.toMap(Person::getName, Person::getAge,(oldValue, newValue) -> newValue,LinkedHashMap::new));
    System.out.println("stream() result :"+map1);
    System.out.println("stream() result :"+map1.getClass());
    System.exit(0); //success
  }
}
查找与匹配

Collectors.counting():计数

Integer[] array = {1, 2, 3};
Long collect = Arrays.stream(array).collect(Collectors.counting());
System.out.println(collect);

Collectors.averagingInt():计算平均值

averagingInt,averagingLong,averagingDouble用法都一样

Integer[] array = {1, 2, 3};
Double collect = Arrays.stream(array).collect(Collectors.averagingInt(x -> x));
System.out.println(collect);

OptionalDouble average = list.stream().mapToInt(Person::getAge).average();

Collectors.summingDouble():计算总和

Integer[] array = {1, 2, 3};
Double collect = Arrays.stream(array).collect(Collectors.summingDouble(x -> x));
System.out.println(collect);

summingInt, summingLong, summingDouble

都是求和不同的方法

int sum = list.stream().collect(summingInt(Person::getAge));
int sum = list.stream().mapToInt(Person::getAge).sum();
int sum = list.stream().map(Person::getAge).reduce(Integer::sum).get();

Collectors.maxBy():获取最大值

Integer[] array = {1, 2, 3};
Optional<Integer> collect = Arrays.stream(array).collect(Collectors.maxBy(Integer::compare));
System.out.println(collect.get());

Optional<Person> optional = list.stream().collect(Collectors.maxBy(Comparator.comparing(Person::getAge)));
Optional<Author> optional = authors.stream().max(Comparator.comparing(Author::getAge));

Collectors.minBy():获取最小值

Integer[] array = {1, 2, 3};
Optional<Integer> collect = Arrays.stream(array).collect(Collectors.minBy(Integer::compare));
System.out.println(collect.get());

Collectors.groupingBy():分组

分组返回集合:

示例1

// 按照年龄分组
Employee e1 = new Employee(1, "小亮");
Employee e2 = new Employee(1, "小红");
Employee e3 = new Employee(2, "小白");
List<Employee> list = new ArrayList<>();
list.add(e1);
list.add(e2);
list.add(e3);
Map<Integer, List<Employee>> collect = list.stream().collect(Collectors.groupingBy(Employee::getAge));
System.out.println(collect);

分组返回集合:
Map<Integer,List<Person>> map = list.stream().collect(Collectors.groupingBy(Person::getAge));

示例2

多级分组:

// 先按照年龄分组,在按照名称分组
Employee e1 = new Employee(1, "小亮");
Employee e2 = new Employee(1, "小红");
Employee e3 = new Employee(1, "小红");
Employee e4 = new Employee(2, "小白");
List<Employee> list = new ArrayList<>();
list.add(e1);
list.add(e2);
list.add(e3);
list.add(e4);
Map<Integer, Map<String, List<Employee>>> collect = list.stream().collect(Collectors.groupingBy(Employee::getAge, Collectors.groupingBy(e -> {
    if ("小红".equals(e.getName())) {
        return "女孩";
    } else {
        return "男孩";
    }
})));
System.out.println(collect);

多级分组:
Map<Integer,Map<T, List<Person>>> map = list.stream().collect(Collectors.groupingBy(Person::getAge, Collectors.groupingBy(Person::getSex)));

按组聚合:

按组聚合:
Map<Integer,Integer> map = list.stream().collect(Collectors.groupingBy(Person::getAgeCollectors.summingInt(Person::getAge)));

Collectors.partitioningBy()

分区;返回值也是Map,符合条件的进入key为true的List集合中,否则进入key为false的List集合中

Integer[] array = {1, 2, 3};
Map<Boolean, List<Integer>> collect = Arrays.stream(array).collect(Collectors.partitioningBy(x -> x > 1));
System.out.println(collect);

在这里插入图片描述
分组是按字段值分,分区是按给定条件分

//根据年龄是否小于等于20来分区
Map<Boolean, List<Person>> map = list.stream().collect(Collectors.partitioningBy(p -> p.getAge() <= 20));
//打印输出
{
false = [Person{name='mike',age=25}, Person{name='tom',age=30}],
true=[Person{name='jack',age=20}]
}

Collectors.summarizingDouble()

作用:

综合性组函数,可以通过方法获取计算数量、计算和、最大值、最小值、平均值

Integer[] array = {1, 2, 3};
DoubleSummaryStatistics collect1 = Arrays.stream(array).collect(Collectors.summarizingDouble(x -> x));
IntSummaryStatistics collect2 = Arrays.stream(array).collect(Collectors.summarizingInt(x -> x));
LongSummaryStatistics collect3 = Arrays.stream(array).collect(Collectors.summarizingLong(x -> x));
System.out.println(collect1.getCount());
System.out.println(collect1.getSum());
System.out.println(collect1.getMax());
System.out.println(collect1.getMin());
System.out.println(collect1.getAverage());

IntSummaryStatistics i = list.stream().collect(summarizingInt(Person::getAge));
getCount()
getMax()
getMin()
getSum()
getAverange()

Collectors.joining()

连接函数,只能针对字符串使用,拼接字符串

// 1、仅仅相连,不加分隔符
String[] array = {"1", "2", "3"};
String collect = Arrays.stream(array).collect(Collectors.joining());
System.out.println(collect);

// 2、相连的时候添加分隔符
String[] array = {"1", "2", "3"};
String collect = Arrays.stream(array).collect(Collectors.joining(","));
System.out.println(collect);

// 3、相连的时候添加分隔符,并且首尾添加符号
String[] array = {"1", "2", "3"};
String collect = Arrays.stream(array).collect(Collectors.joining(",", "》》》", "《《《"));
System.out.println(collect);

String s = list.stream().map(Person::getName).collect(Collectors.joining());
结果:jackmiketom
String s = list.stream().map(Person::getName).collect(Collectors.joining(","));
结果:jack,mike,tom
String s = list.stream().map(Person::getName).collect(Collectors.joining(" and ","Today "," play games."));
结果:Today jack and mike and tom play games.

and是中间插入,Today是开头插入,play games.是末尾插入
在这里插入图片描述

anyMatch

​ 可以用来判断是否有任意符合匹配条件的元素,结果为boolean类型。
这是判断单个是否符合
例子:

​ 判断是否有年龄在29以上的作家

//        判断是否有年龄在29以上的作家
        List<Author> authors = getAuthors();
        boolean flag = authors.stream()
                .anyMatch(author -> author.getAge() > 29);
        System.out.println(flag);
allMatch

​ 可以用来判断是否都符合匹配条件,结果为boolean类型。如果都符合结果为true,否则结果为false。
这是判断全部是否符合
例子:

​ 判断是否所有的作家都是成年人

//        判断是否所有的作家都是成年人
        List<Author> authors = getAuthors();
        boolean flag = authors.stream()
                .allMatch(author -> author.getAge() >= 18);
        System.out.println(flag);
noneMatch

​ 可以判断流中的元素是否都不符合匹配条件。如果都不符合结果为true,否则结果为false

例子:

​ 判断作家是否都没有超过100岁的。

//        判断作家是否都没有超过100岁的。
        List<Author> authors = getAuthors();

        boolean b = authors.stream()
                .noneMatch(author -> author.getAge() > 100);

        System.out.println(b);
findAny

​ 获取流中的任意一个元素。该方法没有办法保证获取的一定是流中的第一个元素。

例子:

​ 获取任意一个年龄大于18的作家,如果存在就输出他的名字

//        获取任意一个年龄大于18的作家,如果存在就输出他的名字
        List<Author> authors = getAuthors();
        Optional<Author> optionalAuthor = authors.stream()
                .filter(author -> author.getAge()>18)
                .findAny();
		//ifPresent是否存在,如果存在就会执行操作,没有也不会出现空指针异常,没有就不会打印不会输出
        optionalAuthor.ifPresent(author -> System.out.println(author.getName()));
findFirst

​ 获取流中的第一个元素。

例子:

​ 获取一个年龄最小的作家,并输出他的姓名。

//        获取一个年龄最小的作家,并输出他的姓名。
        List<Author> authors = getAuthors();
        Optional<Author> first = authors.stream()
                .sorted((o1, o2) -> o1.getAge() - o2.getAge())
                .findFirst();

        first.ifPresent(author -> System.out.println(author.getName()));
reduce归并

​ 对流中的数据按照你指定的计算方式计算出一个结果。(缩减操作可以理解为聚合操作)

reduce的作用是把stream中的元素给组合起来,我们可以传入一个初始值,它会按照我们的计算方式依次拿流中的元素和初始化值进行计算,计算结果再和后面的元素计算。

​ reduce两个参数的重载形式内部的计算方式如下:

两个参数的reduce是初始化的值,element是流中元素的值

一个参数就是将流的第一个元素,变成了初始化值

T result = identity;
for (T element : this stream)
	result = accumulator.apply(result, element)
return result;

在这里插入图片描述

​ 其中identity就是我们可以通过方法参数传入的初始值,accumulator的apply具体进行什么计算也是我们通过方法参数来确定的。

例子:

​ 使用reduce求所有作者年龄的和

//        使用reduce求所有作者年龄的和
        List<Author> authors = getAuthors();
        Integer sum = authors.stream()
                .distinct()
                .map(author -> author.getAge())
                .reduce(0, (result, element) -> result + element);
        System.out.println(sum);
使用reduce求所有作者中年龄的最大值
//        使用reduce求所有作者中年龄的最大值
        List<Author> authors = getAuthors();
        Integer max = authors.stream()
                .map(author -> author.getAge())
                .reduce(Integer.MIN_VALUE, (result, element) -> result < element ? element : result);

        System.out.println(max);
使用reduce求所有作者中年龄的最小值
//        使用reduce求所有作者中年龄的最小值
        List<Author> authors = getAuthors();
        Integer min = authors.stream()
                .map(author -> author.getAge())
                .reduce(Integer.MAX_VALUE, (result, element) -> result > element ? element : result);
        System.out.println(min);

在这里插入图片描述
reduce一个参数的重载形式内部的计算

 	 boolean foundAny = false;
     T result = null;
     for (T element : this stream) {
         if (!foundAny) {
             foundAny = true;
             result = element;
         }
         else
             result = accumulator.apply(result, element);
     }
     return foundAny ? Optional.of(result) : Optional.empty();
如果用一个参数的重载方法去求最小值代码如下:
        //        使用reduce求所有作者中年龄的最小值
        List<Author> authors = getAuthors();
        Optional<Integer> minOptional = authors.stream()
                .map(author -> author.getAge())
                .reduce((result, element) -> result > element ? element : result);
        minOptional.ifPresent(age-> System.out.println(age));

3.5 注意事项

  • 惰性求值(如果没有终结操作,没有中间操作是不会得到执行的)
  • 流是一次性的(一旦一个流对象经过一个终结操作后。这个流就不能再被使用)
  • 不会影响原数据(我们在流中可以多数据做很多处理。但是正常情况下是不会影响原来集合中的元素的。这往往也是我们期望的)

4. Optional

4.1 概述

​ 我们在编写代码的时候出现最多的就是空指针异常。所以在很多情况下我们需要做各种非空的判断。

​ 例如:

        Author author = getAuthor();
        if(author!=null){
            System.out.println(author.getName());
        }

​ 尤其是对象中的属性还是一个对象的情况下。这种判断会更多。

​ 而过多的判断语句会让我们的代码显得臃肿不堪。

​ 所以在JDK8中引入了Optional,养成使用Optional的习惯后你可以写出更优雅的代码来避免空指针异常。

​ 并且在很多函数式编程相关的API中也都用到了Optional,如果不会使用Optional也会对函数式编程的学习造成影响。

4.2 使用

4.2.1 创建对象

​ Optional就好像是包装类,可以把我们的具体数据封装Optional对象内部。然后我们去使用Optional中封装好的方法操作封装进去的数据就可以非常优雅的避免空指针异常。

​ 我们一般使用Optional静态方法ofNullable来把数据封装成一个Optional对象。无论传入的参数是否为null都不会出现问题。
相当于:判断传入的对象是否为空,ofNullable传入null,会报错java.util.NoSuchElementException: No value present

        Author author = getAuthor();
        Optional<Author> authorOptional = Optional.ofNullable(author);

​ 你可能会觉得还要加一行代码来封装数据比较麻烦。但是如果改造下getAuthor方法,让其的返回值就是封装好的Optional的话,我们在使用时就会方便很多。

​ 而且在实际开发中我们的数据很多是从数据库获取的。Mybatis从3.5版本可以也已经支持Optional了。我们可以直接把dao方法的返回值类型定义成Optional类型,MyBastis会自己把数据封装成Optional对象返回。封装的过程也不需要我们自己操作。

​ 如果你确定一个对象不是空的则可以使用Optional静态方法of来把数据封装成Optional对象。

        Author author = new Author();
        Optional<Author> authorOptional = Optional.of(author);

​ 但是一定要注意,如果使用of的时候传入的参数必须不为null。(尝试下传入null会出现什么结果)

​ 如果一个方法的返回值类型是Optional类型。而如果我们经判断发现某次计算得到的返回值为null,这个时候就需要把null封装成Optional对象返回。这时则可以使用Optional静态方法empty来进行封装。

		Optional.empty()
Optional<Employee> optional = Optional.empty();
System.out.println(optional.get());

说明:创建一个空的Optiona实例,在上面代码执行过程中,执行optional.get()方法会出现空指针异常

所以最后你觉得哪种方式会更方便呢?ofNullable

4.2.2 安全消费值

​ 我们获取到一个Optional对象后肯定需要对其中的数据进行使用。这时候我们可以使用其ifPresent方法对来消费其中的值。

​ 这个方法会判断其内封装的数据是否为空,不为空时才会执行具体的消费代码。这样使用起来就更加安全了。

​ 例如,以下写法就优雅的避免了空指针异常。

        Optional<Author> authorOptional = Optional.ofNullable(getAuthor());

        authorOptional.ifPresent(author -> System.out.println(author.getName()));

说明:

如果ofNullable()参数不为空,那么将返回正常的Optional实例,可以正常调用get()方法;
如果ofNullable()参数为null,那么将返回空实例,并且调用get()方法的时候将出现空指针异常;

4.2.3 获取值

​ 如果我们想获取值自己进行处理可以使用get方法获取,但是不推荐。因为当Optional内部的数据为空的时候会出现异常。

4.2.4 安全获取值

​ 如果我们期望安全的获取值。我们不推荐使用get方法,而是使用Optional提供的以下方法。

  • orElseGet

    获取数据并且设置数据为空时的默认值。如果数据不为空就能获取到该数据。如果为空则根据你传入的参数来创建对象作为默认值返回。
    如果Optional中对象不为空,则返回该对象,否则返回orElseGet()方法中Supplier函数式接口返回的参数值

        Optional<Author> authorOptional = Optional.ofNullable(getAuthor());
        Author author1 = authorOptional.orElseGet(() -> new Author());

如果返回的是空,把括号里面的默认值返回
在这里插入图片描述
orElseThrow

获取数据,如果数据不为空就能获取到该数据。如果为空则根据你传入的参数来创建异常抛出。

        Optional<Author> authorOptional = Optional.ofNullable(getAuthor());
        try {
            Author author = authorOptional.orElseThrow((Supplier<Throwable>) () -> new RuntimeException("author为空"));
            System.out.println(author.getName());
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }

如果有值就返回这个值,没有这个值,就抛出自定义的异常
在这里插入图片描述

4.2.5 过滤

​ 我们可以使用filter方法对数据进行过滤。如果原本是有数据的,但是不符合判断,也会变成一个无数据的Optional对象。

        Optional<Author> authorOptional = Optional.ofNullable(getAuthor());
        authorOptional.filter(author -> author.getAge()>100).ifPresent(author -> System.out.println(author.getName()));

4.2.6 判断

​ 我们可以使用isPresent方法进行是否存在数据的判断。如果为空返回值为false,如果不为空,返回值为true。但是这种方式并不能体现Optional的好处,更推荐使用ifPresent方法。ofNullable传入null,会报错java.util.NoSuchElementException: No value present
说明:判断Optional中值是否为空;如果不为空则返回true,否则返回false

        Optional<Author> authorOptional = Optional.ofNullable(getAuthor());

        if (authorOptional.isPresent()) {
            System.out.println(authorOptional.get().getName());
        }

4.2.7 数据转换

​ Optional还提供了map可以让我们的对数据进行转换,并且转换得到的数据也还是被Optional包装好的,保证了我们的使用安全。

例如我们想获取作家的书籍集合。

    private static void testMap() {
        Optional<Author> authorOptional = getAuthorOptional();
        Optional<List<Book>> optionalBooks = authorOptional.map(author -> author.getBooks());
        optionalBooks.ifPresent(books -> System.out.println(books));
    }

orElse()

说明:

如果Optional中对象不为空,则返回该对象,否则返回orElase()方法中的参数值
示例:

Optional<Employee> optional = Optional.ofNullable(null);
System.out.println(optional.orElse(new Employee()));
  1. 函数式接口

5.1 概述

只有一个抽象方法的接口我们称之为函数接口。

​ JDK的函数式接口都加上了**@FunctionalInterface** 注解进行标识。但是无论是否加上该注解只要接口中只有一个抽象方法,都是函数式接口。

5.2 常见函数式接口

  • ​ Consumer 消费接口

    根据其中抽象方法的参数列表和返回值类型知道,我们可以在方法中对传入的参数进行消费。
    在这里插入图片描述
    ​ Function 计算转换接口

根据其中抽象方法的参数列表和返回值类型知道,我们可以在方法中对传入的参数计算或转换,把结果返回
在这里插入图片描述
​ Predicate 判断接口

根据其中抽象方法的参数列表和返回值类型知道,我们可以在方法中对传入的参数条件判断,返回判断结果
在这里插入图片描述
​ Supplier 生产型接口

根据其中抽象方法的参数列表和返回值类型知道,我们可以在方法中创建对象,把创建好的对象返回
在这里插入图片描述

5.3 常用的默认方法

  • and

    我们在使用Predicate接口时候可能需要进行判断条件的拼接。而and方法相当于是使用&&来拼接两个判断条件

    例如:

    打印作家中年龄大于17并且姓名的长度大于1的作家。

        List<Author> authors = getAuthors();
        Stream<Author> authorStream = authors.stream();
        authorStream.filter(new Predicate<Author>() {
            @Override
            public boolean test(Author author) {
                return author.getAge()>17;
            }
        }.and(new Predicate<Author>() {
            @Override
            public boolean test(Author author) {
                return author.getName().length()>1;
            }
        })).forEach(author -> System.out.println(author));
        List<Author> authors = getAuthors();
        Stream<Author> authorStream = authors.stream();
        authorStream.filter(((Predicate<Author>) author -> author.getAge() > 17&&author.getName().length()>1))
                .forEach(author -> System.out.println(author));

在这里插入图片描述
or

我们在使用Predicate接口时候可能需要进行判断条件的拼接。而or方法相当于是使用||来拼接两个判断条件。

例如:

打印作家中年龄大于17或者姓名的长度小于2的作家。

//        打印作家中年龄大于17或者姓名的长度小于2的作家。
        List<Author> authors = getAuthors();
        authors.stream()
                .filter(new Predicate<Author>() {
                    @Override
                    public boolean test(Author author) {
                        return author.getAge()>17;
                    }
                }.or(new Predicate<Author>() {
                    @Override
                    public boolean test(Author author) {
                        return author.getName().length()<2;
                    }
                })).forEach(author -> System.out.println(author.getName()));

negate

Predicate接口中的方法。negate方法相当于是在判断添加前面加了个! 表示取反

例如:

打印作家中年龄不大于17的作家。

//        打印作家中年龄不大于17的作家。
        List<Author> authors = getAuthors();
        authors.stream()
                .filter(new Predicate<Author>() {
                    @Override
                    public boolean test(Author author) {
                        return author.getAge()>17;
                    }
                }.negate()).forEach(author -> System.out.println(author.getAge()));

6. 方法引用

​ 我们在使用lambda时,如果方法体中只有一个方法的调用的话(包括构造方法),我们可以用方法引用进一步简化代码。

6.1 推荐用法

​ 我们在使用lambda时不需要考虑什么时候用方法引用,用哪种方法引用,方法引用的格式是什么。我们只需要在写完lambda方法发现方法体只有一行代码,并且是方法的调用时使用快捷键尝试是否能够转换成方法引用即可。

​ 当我们方法引用使用的多了慢慢的也可以直接写出方法引用。

6.2 基本格式

​ 类名或者对象名::方法名

6.3 语法详解(了解)

6.3.1 引用类的静态方法

​ 其实就是引用类的静态方法

格式

类名::方法名

使用前提

​ 如果我们在重写方法的时候,方法体中只有一行代码,并且这行代码是调用了某个类的静态方法,并且我们把要重写的抽象方法中所有的参数都按照顺序传入了这个静态方法中,这个时候我们就可以引用类的静态方法。


静态说明:
对于函数式接口来说,只允许接口中有一个抽象方法,也就是没有方法体的方法,因此函数式接口中有一个或者多个默认方法或者静态方法也是没有任何问题的,它依然是函数式接口

public class Test {
    public static void main(String[] args) {
        MyInterface.test();
    }
}

interface MyInterface {
    static void test() {
        System.out.println("静态方法");
    }
}

例如:

如下代码就可以用方法引用进行简化

        List<Author> authors = getAuthors();

        Stream<Author> authorStream = authors.stream();
        
        authorStream.map(author -> author.getAge())
                .map(age->String.valueOf(age));

注意,如果我们所重写的方法是没有参数的,调用的方法也是没有参数的也相当于符合以上规则。

优化后如下:

        List<Author> authors = getAuthors();

        Stream<Author> authorStream = authors.stream();

        authorStream.map(author -> author.getAge())
                .map(String::valueOf);

6.3.2 引用对象的实例方法

格式
对象名::方法名
使用前提

​ 如果我们在重写方法的时候,方法体中只有一行代码,并且这行代码是调用了某个对象的成员方法,并且我们把要重写的抽象方法中所有的参数都按照顺序传入了这个成员方法中,这个时候我们就可以引用对象的实例方法

例如:

        List<Author> authors = getAuthors();

        Stream<Author> authorStream = authors.stream();
        StringBuilder sb = new StringBuilder();
        authorStream.map(author -> author.getName())
                .forEach(name->sb.append(name));

优化后:

        List<Author> authors = getAuthors();

        Stream<Author> authorStream = authors.stream();
        StringBuilder sb = new StringBuilder();
        authorStream.map(author -> author.getName())
                .forEach(sb::append);

6.3.4 引用类的实例方法

格式
类名::方法名
使用前提

​ 如果我们在重写方法的时候,方法体中只有一行代码,并且这行代码是调用了第一个参数的成员方法,并且我们把要重写的抽象方法中剩余的所有的参数都按照顺序传入了这个成员方法中,这个时候我们就可以引用类的实例方法。

例如:

    interface UseString{
        String use(String str,int start,int length);
    }

    public static String subAuthorName(String str, UseString useString){
        int start = 0;
        int length = 1;
        return useString.use(str,start,length);
    }
    public static void main(String[] args) {

        subAuthorName("三更草堂", new UseString() {
            @Override
            public String use(String str, int start, int length) {
                return str.substring(start,length);
            }
        });

	}

优化后如下:

    public static void main(String[] args) {

        subAuthorName("三更草堂", String::substring);

    }

6.3.5 构造器引用

​ 如果方法体中的一行代码是构造器的话就可以使用构造器引用。

格式
类名::new
使用前提

​ 如果我们在重写方法的时候,方法体中只有一行代码,并且这行代码是调用了某个类的构造方法,并且我们把要重写的抽象方法中的所有的参数都按照顺序传入了这个构造方法中,这个时候我们就可以引用构造器。

例如:

        List<Author> authors = getAuthors();
        authors.stream()
                .map(author -> author.getName())
                .map(name->new StringBuilder(name))
                .map(sb->sb.append("-三更").toString())
                .forEach(str-> System.out.println(str));

优化后:

        List<Author> authors = getAuthors();
        authors.stream()
                .map(author -> author.getName())
                .map(StringBuilder::new)
                .map(sb->sb.append("-三更").toString())
                .forEach(str-> System.out.println(str));

7. 高级用法

基本数据类型优化

​ 我们之前用到的很多Stream的方法由于都使用了泛型。所以涉及到的参数和返回值都是引用数据类型。

​ 即使我们操作的是整数小数,但是实际用的都是他们的包装类。JDK5中引入的自动装箱和自动拆箱让我们在使用对应的包装类时就好像使用基本数据类型一样方便。但是你一定要知道装箱和拆箱肯定是要消耗时间的。虽然这个时间消耗很下。但是在大量的数据不断的重复装箱拆箱的时候,你就不能无视这个时间损耗了。

​ 所以为了让我们能够对这部分的时间消耗进行优化。Stream还提供了很多专门针对基本数据类型的方法。

​ 例如:mapToInt,mapToLong,mapToDouble,flatMapToInt,flatMapToDouble等。

    private static void test27() {

        List<Author> authors = getAuthors();
        authors.stream()
                .map(author -> author.getAge())
                .map(age -> age + 10)
                .filter(age->age>18)
                .map(age->age+2)
                .forEach(System.out::println);

        authors.stream()
                .mapToInt(author -> author.getAge())
                .map(age -> age + 10)
                .filter(age->age>18)
                .map(age->age+2)
                .forEach(System.out::println);
    }

求和:sum()

        List<String> list = new ArrayList<String>();
        list.add("1");
        list.add("2");
        list.add("3");
        double sum = list.stream().mapToDouble(Double::valueOf).sum();
        System.out.println(sum);

平均值:average

        List<String> list = new ArrayList<String>();
        list.add("1");
        list.add("2");
        list.add("3");
        double asDouble = list.stream().mapToDouble(Double::valueOf).average().getAsDouble();
        System.out.println(asDouble);

并行流

​ 当流中有大量元素时,我们可以使用并行流去提高操作的效率。其实并行流就是把任务分配给多个线程去完全。如果我们自己去用代码实现的话其实会非常的复杂,并且要求你对并发编程有足够的理解和认识。而如果我们使用Stream的话,我们只需要修改一个方法的调用就可以使用并行流来帮我们实现,从而提高效率。

​ parallel方法可以把串行流转换成并行流。
parallel:相当于开启多线程,多个线程同时进行处理
peek:是中间操作,可以在任意一个地方进行操作,用来调试的,进行日志的输出

    private static void test28() {
        Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
        Integer sum = stream.parallel()
                .peek(new Consumer<Integer>() {
                    @Override
                    public void accept(Integer num) {
                        System.out.println(num+Thread.currentThread().getName());
                    }
                })
                .filter(num -> num > 5)
                .reduce((result, ele) -> result + ele)
                .get();
        System.out.println(sum);
    }

也可以通过parallelStream直接获取并行流对象。

        List<Author> authors = getAuthors();
        authors.parallelStream()
                .map(author -> author.getAge())
                .map(age -> age + 10)
                .filter(age->age>18)
                .map(age->age+2)
                .forEach(System.out::println);

并行流和串行流的区别

串行流使用一个线程去做事情,而并行流使用了ForkJoin框架,可以进行线程拆分、汇总、窃取等操作,用以加快CPU运行效率,在Java8中默认使用串行流,也就是顺序流。通过parallel()方法可以使用并行流,通过sequential()方法可以使用顺序流,示例如下

// 1、并行流
long sum = LongStream.rangeClosed(0, 100000000000L).parallel().reduce(0, Long::sum);

// 2、串行流
long sum = LongStream.rangeClosed(0, 100000000000L).sequential().reduce(0, Long::sum);

1.5、JDK1.7下使用ForkJoin框架计算0~100000000000之和

public class Test {
    public static void main(String[] args) {
    	// 计时
        Instant start = Instant.now();
        // 池
        ForkJoinPool pool = new ForkJoinPool();
        MyForkJoin myForkJoin = new MyForkJoin(0L, 100000000000L);
        // 执行
        Long sum = pool.invoke(myForkJoin);
        // 打印结果
        System.out.println("加和结果:" + sum);
        // 打印耗费时间
        Instant end = Instant.now();
        System.out.println("耗费时间:" + Duration.between(start, end).toMillis() + "ms");
    }
}

class MyForkJoin extends RecursiveTask<Long> {

    private Long start;

    private Long end;

    public MyForkJoin(Long start, Long end) {
        this.start = start;
        this.end = end;
    }

	// 截止条件(门槛)
    private static final long THRESHOLD = 10000L;

    @Override
    protected Long compute() {
        if (end - start <= THRESHOLD) {
            long sum = 0L;
            for (long i = start; i <= end; i++) {
                sum += i;
            }
            return sum;
        } else {
            long middle = (start + end) / 2;

            // 左侧拆分
            MyForkJoin left = new MyForkJoin(start, middle);
            left.fork();

            // 右侧拆分
            MyForkJoin right = new MyForkJoin(middle + 1, end);
            right.fork();

            // 汇总
            return left.join() + right.join();
        }
    }
}

1.6、JDK1.8下使用并行流计算0~100000000000之和

public class Test {
    public static void main(String[] args) {
        // 计时
        Instant start = Instant.now();
        // 计算
        long sum = LongStream.rangeClosed(0, 100000000000L).parallel().reduce(0, Long::sum);
        // 打印结果
        System.out.println("加和结果:" + sum);
        // 打印耗费时间
        Instant end = Instant.now();
        System.out.println("耗费时间:" + Duration.between(start, end).toMillis() + "ms");
    }
}

rangeClosed 第一个参数接受起始值,第二个参数接受结束值。rangeClosed包含结束值。

IntStream is = IntStream.rangeClosed(1, 100).filter(i -> i % 2 == 0);
is.forEach(System.out::println);
2
4
6
8
......
96
98
100

range 第一个参数接受起始值,第二个参数接受结束值。range是不包含结束值的

LongStream ls = LongStream.range(1, 100);
ls.forEach(System.out::println);
1
2
3
4
......
97
98
99

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/968340.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Python常用IDE选择与安装

1、IDE简介 选择一款高效而又顺手的IDE学习或使用Python&#xff0c;可以让你的开发之路充满激情和动力&#xff0c;让你真正投入其中。 常见的Python的IDE工具有&#xff1a; PyCharm 由JetBrains开发的Python IDE&#xff0c;功能强大&#xff0c;支持调试、代码自动完成、…

Java应用CPU占用过高故障排除

一、背景 最近测试反馈测试环境接口偶现有访问超时&#xff0c;然后APP提示是网络失败&#xff0c;看了一下测试环境的应用完全没啥问题&#xff0c;一直以为是网络问题。 今天测试有反馈了&#xff0c;赶紧看了一下测试服务器&#xff0c;这次终于有症状了&#xff0c;CPU直…

【FPGA项目】沙盘演练——基础版报文收发

​​​​​​​ ​​​​​​​ ​​​​​​​ ​​​​​​​ ​​​​​​​ 第1个虚拟项目 前言 点灯开启了我们的FPGA之路&#xff0c;那么我们来继续沙盘演练。 用一个虚拟项目&#xff0c;来入门练习&#xff0c;以此步入数字逻辑的…

《C++ primer plus》精炼(OOP部分)——对象和类(1)

聪明在于学习&#xff0c;天才在于积累。所谓天才&#xff0c;实际上是依靠学习。 文章目录 概述正文面向对象编程和面向过程编程类和对象类的组成公共接口 类声明访问控制封装 类和结构体类定义 概述 C是门包罗万象的语言&#xff0c;它将各门类的编程思想杂糅&#xff0c;最…

Parallels Desktop 19新功能解析,助力跨平台工作流程提升!

对于许多Mac用户来说&#xff0c;运行Windows应用程序是必不可少的。也许你的雇主使用的软件只适用于Windows&#xff0c;或者需要使用依赖于某些Windows技术的网站。或者你想在Mac上玩Windows游戏。或者&#xff0c;你可能需要在其他操作系统上测试应用程序和服务——你可以在…

系统vcomp120.dll丢失怎么办?要怎样修复呢?三个修复方法分享

我要和大家分享一个关于系统丢失vcomp120.dll文件的问题。这个问题可能会困扰很多使用电脑的朋友&#xff0c;特别是在运行某些软件时&#xff0c;可能会出现“找不到vcomp120.dll”的错误提示。那么&#xff0c;遇到这样的问题&#xff0c;我们应该如何解决呢&#xff1f;接下…

用 ChatGPT 写代码太省时间了

几个月前&#xff0c;我们聊过陶哲轩使用 ChatGPT 辅助解决数学问题。当时&#xff0c;他觉得虽然测试结果不太令人满意&#xff0c;但也并没有对 ChatGPT 持完全否定的态度。他觉得&#xff0c;像 ChatGPT 这类大型语言模型在数学中可以用来做一些半成品的语义搜索工作&#x…

【leetcode 力扣刷题】汇总区间//合并区间//插入区间

一些关于区间的力扣题目 228. 汇总区间56. 合并区间57. 插入区间 228. 汇总区间 题目链接&#xff1a;228.汇总区间 题目内容&#xff1a; 看题目真是没懂这个题到底是要干啥……实际上题目要求的恰好覆盖数组中所有数字的最小有序区间范围列表&#xff0c;这个最小是指一个区…

QT 设置应用程序图标

1.下载xx.ico图标&#xff1a;ico网址 2.在线PNG转换ICO&#xff1a;png在线转换ico 3.添加图标资源 1&#xff09;新建文件路径 2&#xff09;添加图片资源 3&#xff09;在 .pro文件里面添加图片 4&#xff09;将xx.ico放到工程目录&#xff0c;编译完可以看到xx.exe的图标…

【LeetCode】515.在每个树行中找最大值

题目 给定一棵二叉树的根节点 root &#xff0c;请找出该二叉树中每一层的最大值。 示例1&#xff1a; 输入: root [1,3,2,5,3,null,9] 输出: [1,3,9]示例2&#xff1a; 输入: root [1,2,3] 输出: [1,3]提示&#xff1a; 二叉树的节点个数的范围是 [0,10^4]-2^31 < No…

无涯教程-JavaScript - CUBERANKEDMEMBER函数

描述 CUBERANKEDMEMBER函数返回集合中的第n个或排序的成员。 使用此功能可返回一组中的一个或多个元素,如销售业绩最好的人或前十名的学生。 语法 CUBERANKEDMEMBER (connection, set_expression, rank, [caption])争论 Argument描述Required/OptionalconnectionThe name …

Linux--进程--创建子进程一般目的

父进程创建子进程的目的&#xff1a;简单来说&#xff1a;给特定的输入&#xff0c;给出特定的输出 父进程希望复制自己&#xff0c;使父、子进程同时执行不同的代码段。这在网络服务进程中是常见的——父进程等待客户端的服务请求。当请求到达&#xff0c;父进程调用fork&…

FPGA 学习笔记:Vivado 工程管理技巧

前言 当前使用 Xilinx 的 FPGA,所以需要熟悉 Xilinx FPGA 的 开发利器 Vivado 的工程管理方法 这里初步列举一些实际 Xilinx FPGA 开发基于 Vivado 的项目使用到的工程的管理技巧 代码管理 做过嵌入式软件或者其他软件开发的工程技术人员,都会想到使用代码管理工具,如 SVN 、…

架构师成长之路Redis第一篇|Redis 安装介绍以及内存分配器jemalloc

安装 Redis官网:https://redis.io/download/ 下载安装二进制文件 可下载安装最新版Redis7.2.0,或者可选版本6.x 我这里下载6.2.13和7.2最新版本,后面我们都是安装6.2.13版本的信息进行讲解 二进制文件安装步骤 安装前期准备: 安装gcc yum install gcc 压缩文件 tar -xzf re…

SpringCloud(34):Nacos服务发现

1 从单体架构到微服务 1.1单体架构 Web应用程序发展的早期,大部分web工程师将所有的功能模块打包到一起并放在一个web容器中运行,所有功能模块使用同一个数据库,同时,它还提供API或者UI访问的web模块等。 尽管也是模块化逻辑,但是最终它还是会打包并部署为单体式应用,这…

Linux:内核解压缩过程简析

文章目录 1. 前言2. 背景3. zImage 的构建过程4. 内核引导过程5. 内核解压缩过程6. 内核加压缩过程小结7. 参考资料 1. 前言 限于作者能力水平&#xff0c;本文可能存在谬误&#xff0c;因此而给读者带来的损失&#xff0c;作者不做任何承诺。 2. 背景 本文基于 ARM32架构 …

【德哥说库系列】-ASM管理Oracle 19C单实例部署

&#x1f4e2;&#x1f4e2;&#x1f4e2;&#x1f4e3;&#x1f4e3;&#x1f4e3; 哈喽&#xff01;大家好&#xff0c;我是【IT邦德】&#xff0c;江湖人称jeames007&#xff0c;10余年DBA及大数据工作经验 一位上进心十足的【大数据领域博主】&#xff01;&#x1f61c;&am…

垃圾回收 - 引用计数法

GC原本是一种“释放怎么都无法被引用的对象的机制”。那么人们自然而然就会想到&#xff0c;可以让所有对象事先记录下“有多少程序引用了自己”。让各对象知道自己的“人气指数”&#xff0c;从而让没有人气的对象自己消失&#xff0c;这就是引用计数法。 1、计数器 计数器表…

【Unity基础】1.项目搭建与视图编辑

【Unity基础】1.项目搭建与视图编辑 大家好&#xff0c;我是Lampard~~ 欢迎来到Unity基础系列博客&#xff0c;终于要开始写基础系列的博客了&#xff0c;前两篇的内容基本上与入门系列相同&#xff0c;如果有紧跟入门系列的同学可以直接从第三篇文章开始看 好了话不多说我们开…

Java切换到Kotlin,Crash率上升了?

前言 最近对一个Java写的老项目进行了部分重构&#xff0c;测试过程中波澜不惊&#xff0c;顺利上线后几天通过APM平台查看发现Crash率上升了&#xff0c;查看堆栈定位到NPE类型的Crash&#xff0c;大部分发生在Java调用Kotlin的函数里&#xff0c;本篇将会分析具体的场景以及…