Lambda表达式和Stream流(Java8新特性)

目录

一、Lambda表达式

1、Lambda表达式语法 :(参数)->{具体实现}

2、方法引用ObjectReference::methodName

二、Stream流

1、Stream流特性

2、使用流

(1)筛选和切片

(2)映射——map()和flatMap()

(3)查找和匹配

(4)归约

(5)数值流

3、综合练习代码示例

(1)综合练习一

(2)综合练习二


一、Lambda表达式

1、Lambda表达式语法 :(参数)->{具体实现}

用() -> {}代码块替代了整个匿名类

public class LambdaDemo {
    public static void main(String[] args) {
        // 内部类方式
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("内部类方式...");
            }
        }).start();

        // lambda方式
        new Thread(()-> System.out.println("lambada方式...")).start();
    }
}

不能省略大括号情况:

  • 方法体有返参时不能省略大括号
  • 方法体有多行逻辑代码时不能省略大括号

省略小括号情况:

  • 方法只有一个参数,而且这个参数的类型可以推导出,那么可以省略小括号

2、方法引用ObjectReference::methodName

方法引用是lambda表达式的一个简化写法,引用的方法其实是表达式的方法体实现。

语法非常简单,左边是容器(类名,实例名):: 右边则是相应的方法名

public class LambdaDemo {
    public static void main(String[] args) {
        String[] stringArray = { "Barbara", "James", "Mary", "John",
                "Patricia", "Robert", "Michael", "Linda" };
        // 引用:隐式传参,表达式什么都没做,仅仅是调用了一个已经存在的方法
        // 作用:使代码更加紧凑
        // 需要由兼容的函数式接口构成的目标类型上下文
        Arrays.stream(stringArray).sorted(String::compareToIgnoreCase).forEach(System.out::println);
    }
}

方法引用不需要显式的传递参数,方法引用中会自动传递参数;Lambda表达式什么都没做,仅仅是调用了一个已经存在的方法

方法引用是用来直接访问类或者实例的已经存在的方法或者构造方法。方法引用提供了一种引用而不执行方法的方式,它需要由兼容的函数式接口构成的目标类型上下文。计算时,方法引用会创建函数式接口的一个实例。

示例:创建Employee类

public class Employee {

    private String name;

    private int salary;

    private String office;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getSalary() {
        return salary;
    }

    public void setSalary(int salary) {
        this.salary = salary;
    }

    public String getOffice() {
        return office;
    }

    public void setOffice(String office) {
        this.office = office;
    }

    public Employee(String name, int salary, String office) {
        this.name = name;
        this.salary = salary;
        this.office = office;
    }
}

新增方法:

    // 向Employee中增加此方法    
    public static void printInformation(Employee employee){
        System.out.println(" name:"+employee.getName()+
                " office:"+employee.getOffice()+
                " salary:"+employee.getSalary());
    }

    public static void main(String[] args) {
        List employees = Arrays.asList(
                new Employee("Matt", 5000, "New York"),
                new Employee("Steve", 6000, "London"),
                new Employee("Carrie", 10000, "New York"));
        // 方法引用——调用一个已经存在的方法
        employees.stream().forEach(Employee::printInformation);
    }

测试结果:

 name:Matt office:New York salary:5000
 name:Steve office:London salary:6000
 name:Carrie office:New York salary:10000

上边引用Employee::printInformation相当于lambda表达式x->printInformation(x)

引用格式:

1)静态方法:ClassName::methodName。例如 Object::equals
2)实例方法:Instance::menthodName。   例如 Object obj =new Object();obj::equals
3)构造函数:ClassName::new

二、Stream流

1、Stream流特性

  • Stream流不是一种数据结构,不保存数据,它只是在原数据集上定义了一组操作。
  • 这些操作是惰性的,即每当访问到流中的一个元素,才会在此元素上执行这一系列操作。
  • Stream不保存数据,故每个Stream流只能使用一次。

应用在Stream流上的操作,可以分成两种:Intermediate(中间操作)Terminal(终止操作)。中间操作的返回结果都是Stream,故可以多个中间操作叠加;终止操作用于返回我们最终需要的数据,只能有一个终止操作。

流的Intermediate方法(中间操作)

  • filter(Predicate)  将结果为false的元素过滤掉
  • map(fun)  转换元素的值,可以用方法引元或者lambda表达式
  • flatMap(fun)  若元素是流,将流摊平为正常元素,再进行元素转换
  • limit(n)  保留前n个元素
  • skip(n)  跳过前n个元素
  • distinct()  剔除重复元素
  • sorted()  将Comparable元素的流排序
  • sorted(Comparator)  将流元素按Comparator排序
  • peek(fun)  流不变,但会把每个元素传入fun执行,可以用作调试

流的Terminal方法(终结操作)

(1)约简操作

  • max(Comparator)
  • min(Comparator)
  • count()
  • findFirst()  返回第一个元素
  • findAny()  返回任意元素
  • anyMatch(Predicate)  任意元素匹配时返回true
  • allMatch(Predicate)  所有元素匹配时返回true
  • noneMatch(Predicate)  没有元素匹配时返回true
  • reduce(fun)  从流中计算某个值,接受一个二元函数作为累积器,从前两个元素开始持续应用它,累积器的中间结果作为第一个参数,流元素作为第二个参数
  • reduce(a, fun)  a为初始值,作为累积器的起点
  • reduce(a, fun1, fun2)  与二元变形类似,并发操作中,当累积器的第一个参数与第二个参数都为流元素类型时,可以对各个中间结果也应用累积器进行合并,但是当累积器的第一个参数不是流元素类型而是类型T的时候,各个中间结果也为类型T,需要fun2来将各个中间结果进行合并

(2)收集操作

  • iterator()
  • forEach(fun)
  • forEachOrdered(fun)  可以应用在并行流上以保持元素顺序
  • toArray()
  • toArray(T[] :: new)  返回正确的元素类型
  • collect(Collector)
  • collect(fun1, fun2, fun3)  fun1转换流元素;fun2为累积器,将fun1的转换结果累积起来;fun3为组合器,将并行处理过程中累积器的各个结果组合起来
     

2、使用流

(1)筛选和切片

filter()过滤:该操作会接受一个谓词作为参数,并返回一个包含所有符合谓词元素的流

distinct()去重:返回一个元素各异的流(根据流所生成元素的hashCode 和 equals 方法实现)

limit(n)截取:返回一个不超过给定长度的流。所需的长度作为参数传递给limit 。如果流是有序的,则最多会返回前 n 个元素。

skip(n)跳过:返回一个扔掉了前 n 个元素的流。如果流中元素不足 n 个,则返回一个空流。请注意,limit(n)和skip(n)是互补的!

演示代码:

public class EmployeeDemo {
    public static void main(String[] args) {
        List employees = Arrays.asList(
                new Employee("Matt", 5000, "New York"),
                new Employee("Steve", 6000, "London"),
                new Employee("Carrie", 10000, "New York"),
                new Employee("Peter", 7000, "New York"),
                new Employee("Alec", 6000, "London"),
                new Employee("Sarah", 8000, "London"),
                new Employee("Rebecca", 4000, "New York"),
                new Employee("Pat", 20000, "New York"),
                new Employee("Tammy", 9000, "New York"),
                new Employee("Fred", 15000, "Tokyo"));

        // 过滤filter(),筛选薪水超过6000的员工
        System.out.println("----------过滤filter(),筛选薪水超过6000的员工----------");
        employees.stream()
                .filter(x->x.getSalary()>6000)
                .map(Employee::getName)
                .forEach(System.out::println);

        // 去重distinct(),获取所有城市列表
        System.out.println("----------去重distinct(),获取所有城市列表----------");
        employees.stream()
                .map(Employee::getOffice)
                .distinct()
                .forEach(System.out::println);

        // 截取limit(n),截取工资排名前3的员工信息
        System.out.println("----------截取limit(n),截取工资排名前3的员工信息----------");
        Map limitMap = employees.stream()
                .sorted(Comparator.comparing(Employee::getSalary).reversed())
                .limit(3)
                .collect(Collectors.toMap(Employee::getName, Employee::getSalary));
        limitMap.forEach((x,y)->{
                    System.out.println("姓名:"+x+" 薪水:"+y);
                });

        // 跳过元素skip(n),跳过薪水排名前3的员工信息
        System.out.println("----------跳过元素skip(n),跳过薪水排名前3的员工信息----------");
        Map skipMap = employees.stream().sorted(Comparator.comparing(Employee::getSalary).reversed())
                .skip(3)
                .collect(Collectors.toMap(Employee::getName, Employee::getSalary));
        skipMap.forEach((x,y)->{
            System.out.println("姓名:"+x+" 薪水:"+y);
        });
    }
}

演示结果:

----------过滤filter(),筛选薪水超过6000的员工----------
Carrie
Peter
Sarah
Pat
Tammy
Fred
----------去重distinct(),获取所有城市列表----------
New York
London
Tokyo
----------截取limit(n),截取工资排名前3的员工信息----------
姓名:Pat 薪水:20000
姓名:Fred 薪水:15000
姓名:Carrie 薪水:10000
----------跳过元素skip(n),跳过薪水排名前3的员工信息----------
姓名:Matt 薪水:5000
姓名:Tammy 薪水:9000
姓名:Steve 薪水:6000
姓名:Sarah 薪水:8000
姓名:Alec 薪水:6000
姓名:Rebecca 薪水:4000
姓名:Peter 薪水:7000

(2)映射——map()和flatMap()

map():它会接受一个函数作为参数,这个函数会被应用到每个元素上。并将其映射成一个新的元素。

public class Demo {
    public static void main(String[] args) {
        List employees = Arrays.asList(
                new Employee("Matt", 5000, "New York"),
                new Employee("Steve", 6000, "London"),
                new Employee("Carrie", 10000, "New York"),
                new Employee("Fred", 15000, "Tokyo"));

        // 收集名字,映射成一个名单列表
        List nameList = employees.stream()
                .map(Employee::getName)
                .collect(toList());
        nameList.forEach(System.out::println);

        // 多个映射操作,获取名单名字的长度
        List lengthList = employees.stream()
                .map(Employee::getName)// 获取名字映射
                .map(String::length)// 通过名字映射再获取名字长度的映射
                .collect(toList());// 收集
        lengthList.forEach(System.out::println);
    }
}

演示结果:

Matt
Steve
Carrie
Fred
4
5
6
4

flatMap(Arrays::stream) :流的扁平化,把两个流合并起来,即扁平化为一个流。

public class Demo {
    public static void main(String[] args) {
        /**
         * 给定单词列表:[“Hello”,”World”]
         * 求返回列表:[“H”,”e”,”l”,“o”,”W”,”r”,”d”]
         */
        List lists = Arrays.asList("Hello","world");

        // 使用普通map
        // 这个方法的问题在于,传递给map方法的Lambda为每个单词返回了一个String[](String列表)。
        List maplist = lists.stream()
                .map(x -> x.split(""))// 只使用map
                .distinct()
                .collect(toList());
        System.out.println("普通map:"+maplist);

        // 测试Arrays.stream()方法效果
        // 接受一个数组并产生一个流,流对象
        List> streamlist = lists.stream()
                .map(x -> x.split(""))
                .map(Arrays::stream)
                .distinct()
                .collect(toList());
        System.out.println("Arrays.stream()方法:"+streamlist);

        // 使用扁平流flatMap()
        List flatMapList = lists.stream()
                .map(x -> x.split(""))
                .flatMap(Arrays::stream)// 使用扁平流
                .distinct()
                .collect(toList());
        System.out.println("扁平流:"+flatMapList);
    }
}

测试结果:

可以看出,Arrays.stream() 的方法可以接受一个数组并产生一个流。

而 flatMap(Arrays::stream) 的效果就是把两个流合并起来,即扁平化为一个流。

(3)查找和匹配

allMatch() : 匹配所有,true

anyMatch() :任意匹配,true

noneMatch() :没有匹配,true

findFirst() :查找第一个

findAny(): 查找任意一个

代码示例:

public class EmployeeDemo {
    public static void main(String[] args) {
        List employees = Arrays.asList(
                new Employee("Matt", 5000, "New York"),
                new Employee("Steve", 6000, "London"),
                new Employee("Carrie", 10000, "New York"),
                new Employee("Peter", 7000, "New York"),
                new Employee("Alec", 6000, "London"),
                new Employee("Sarah", 8000, "London"),
                new Employee("Rebecca", 4000, "New York"),
                new Employee("Pat", 20000, "New York"),
                new Employee("Tammy", 9000, "New York"),
                new Employee("Fred", 15000, "Tokyo"));

        /**
         * 匹配元素
         * allMatch():匹配所有
         * anyMatch():任意匹配
         * noneMatch():没有匹配
         */
        System.out.println("----------匹配元素----------");
        // allMatch():匹配所有
        boolean allTokyo = employees.stream()
                .allMatch(x -> x.getOffice().equals("Tokyo"));
        System.out.println("所有的办公地点都是Tokyo吗?:"+allTokyo);

        // anyMatch():任意匹配
        boolean anyTokyo = employees.stream()
                .anyMatch(x -> x.getOffice().equals("Tokyo"));
        System.out.println("存在办公地点为Tokyo吗?:"+anyTokyo);

        // noneMatch():没有匹配
        boolean noneTokyo = employees.stream()
                .noneMatch(x -> x.getOffice().equals("Tokyo"));
        System.out.println("所有办公地点都不是Tokyo吗?:"+noneTokyo);

        /**
         * 查找元素
         * findFirst():查找第一个
         * findAny(): 查找任意一个
         */
        System.out.println("----------查找元素----------");
        // findAny(): 查找任意一个
        Optional any = employees.stream()
                .filter(x -> x.getSalary() > 10000)
                .findAny();
        System.out.println(any+":员工名:"+any.get().getName()+" 薪水:"+any.get().getSalary());

        // findFirst():查找第一个
        Optional first = employees.stream()
                .filter(x -> x.getSalary() > 10000)
                .findFirst();
        System.out.println(first+":员工名:"+first.get().getName()+" 薪水:"+first.get().getSalary());
    }
}

示例结果:

Lambda表达式和Stream流(Java8新特性)_第1张图片

可以看到 findAny() 返回一个 Optional 类型的值,Optional 类( java.util.Optional )是一个容器类,代表一个值存在或不存在。

Optional 里面几种可以迫使你显式地检查值是否存在或处理值不存在的情形的方法:

* isPresent() 将在 Optional 包含值的时候返回 true , 否则返回 false 。 
* ifPresent(Consumer block) 会在值存在的时候执行给定的代码块。
* T get() 会在值存在时返回值,否则抛出一个 NoSuchElement 异常。 
* T orElse(T other) 会在值存在时返回值,否则返回一个默认值。

为什么会同时有 findFirst 和 findAny 呢?

答案是并行。找到第一个元素在并行上限制更多。如果你不关心返回的元素是哪个,请使用 findAny ,因为它在使用并行流时限制较少。

(4)归约

1)数值求和

使用lambda表达式和不使用lambda表达式的累加求和

public class Demo {
    public static void main(String[] args) {
        List integers = Arrays.asList(1, 2, 3, 4, 5);
        // 不使用lambda表达式
        int sum = 0;
        for(Integer in : integers){
            sum += in;
        }
        System.out.println("不使用lambda表达式sum:"+sum);

        // lambda表达式归约
        Integer reduce = integers.stream().reduce(0, (a, b) -> a + b);
        System.out.println("reduce归约表达式求和:"+reduce);
    }
}

演示结果:

Lambda表达式和Stream流(Java8新特性)_第2张图片

reduce 接受两个参数: 
* 一个初始值,这里是0 
* 一个 BinaryOperator用来将两个元素结合起来产生一个新值,这里我们用的是lambda (a, b) -> a + b

上面的操作效果我们可以理解为:把初始值和后面的函数每一次计算的结果相加求和

public class Demo {
    public static void main(String[] args) {
        List integers = Arrays.asList(1, 2, 3, 4, 5);
        // lambda表达式归约
        Integer reduce = integers.stream().reduce(1, (a, b) -> a * b);
        System.out.println("reduce归约表达式求积:"+reduce);
    }
}

这个操作效果我们可以理解为:把初始值和后面的函数每一次计算的结果相乘。Lambda反复结合每个元素,直到流被归约成一个值。

通过这两个例子我们可以看到:reduce两个参数的运算方式取决于后面Lambda的运算方式

在java8中Integer类多了几个静态方法:

    /**
     * Adds two integers together as per the + operator.
     *
     * @param a the first operand
     * @param b the second operand
     * @return the sum of {@code a} and {@code b}
     * @see java.util.function.BinaryOperator
     * @since 1.8
     */
    public static int sum(int a, int b) {
        return a + b;
    }

    /**
     * Returns the greater of two {@code int} values
     * as if by calling {@link Math#max(int, int) Math.max}.
     *
     * @param a the first operand
     * @param b the second operand
     * @return the greater of {@code a} and {@code b}
     * @see java.util.function.BinaryOperator
     * @since 1.8
     */
    public static int max(int a, int b) {
        return Math.max(a, b);
    }

    /**
     * Returns the smaller of two {@code int} values
     * as if by calling {@link Math#min(int, int) Math.min}.
     *
     * @param a the first operand
     * @param b the second operand
     * @return the smaller of {@code a} and {@code b}
     * @see java.util.function.BinaryOperator
     * @since 1.8
     */
    public static int min(int a, int b) {
        return Math.min(a, b);
    }

通过结合以上方法,结合lambda表达式,使求和代码看起来更加简洁

public class Demo {
    public static void main(String[] args) {
        List integers = Arrays.asList(1, 2, 3, 4, 5);
        // lambda表达式归约
        Integer reduce = integers.stream().reduce(0, Integer::sum);
        System.out.println("reduce归约表达式求和:"+reduce);
    }
}

无初始值

reduce还有一个重载的载体,它不接受初始值,但是会返回一个 Optional 对象。

public class Demo {
    public static void main(String[] args) {
        List integers = Arrays.asList(1, 2, 3, 4, 5);
        // lambda表达式归约求和——无初始值
        Optional reduce = integers.stream().reduce(Integer::sum);
        System.out.println("reduce归约表达式求和:"+reduce);
        // 调用get()方法获取具体值
        Integer integer = reduce.get();
        System.out.println("打印求和值:"+integer);
    }
}

演示结果:

Lambda表达式和Stream流(Java8新特性)_第3张图片

为什么它返回一个Optional呢?考虑到流中没有值的情况,reduce操作便无法返回其和,因为没有初始值。这就是为什么结果被包裹在一个Optional对象里,以表明结果可能不存在。

值不存在的情况:

public class Demo {
    public static void main(String[] args) {
        List integers = new ArrayList<>();
        // lambda表达式归约求和——无初始值
        Optional reduce = integers.stream().reduce(Integer::sum);
        System.out.println("reduce归约表达式求和:"+reduce);
        // 调用get()方法获取具体值
        Integer integer = reduce.get();
        System.out.println("打印求和值:"+integer);
    }
}

演示结果:

Lambda表达式和Stream流(Java8新特性)_第4张图片

2)求最大值和最小值

public class Demo {
    public static void main(String[] args) {
        List integers = Arrays.asList(1, 2, 3, 4, 5);
        // lambda表达式归约求最大值
        Optional max = integers.stream().reduce(Integer::max);
        // lambda表达式归约求最小值
        Optional min = integers.stream().reduce(Integer::min);
        System.out.println("reduce归约表达式求最大值:"+max+" reduce归约表达式求最小值:"+min);
    }
}

演示结果:

给定两个元素能够返回最大值的Lambda,redce会考虑流中的新值和下一个元素,并产生一个新的最大值,直到整个流消耗完。

3)归约方法的优势与并行化
相比于java8之前的迭代求和,我们使用reduce的好处在于,这里的迭代被内部迭代抽象掉了,这让内部迭代可以选择并行执行reduce操作.。而迭代求和的例子要更新共享变量sum,这不是那么容易并行化的.。
如果你加入了同步,可能会发现线程竞争抵消掉了并行本应该带来的性能提升。

这种计算的并行化需要另一种方法:将输入分块,再分块求和,最后再合并起来。

这正式reduce所提供的,使用流来对所有的元素并行求和时,你的代码几乎不用修改: stream() 换成了 parallelStream()。

(5)数值流

先看一下下边代码存在的问题:

        // 第一种方式:工资前3员工的薪水总和计算
        Optional salarySum = employees.stream()
                .sorted(Comparator.comparing(Employee::getSalary).reversed())
                .limit(3)
                .map(Employee::getSalary)
                .reduce(Integer::sum);// Integer都必须拆箱成一个原始(int)类型
        System.out.println("工资前3员工的薪水总和计算:"+salarySum);

演示结果:

这段代码的问题是,它有一个暗含的装箱成本。每个 Integer 都必须拆箱成一个原始类型,再进行求和。

Java 8引入了三个原始类型特化流接口来解决这个问题:

原始类型流特化

IntStream,DoubleStream,LongStream分别将流中的元素特化为 int ,double,long ,从而避免了暗含的装箱成本。

每个接口都带来了进行常用数值归约的新方法,比如对数值流求和的 sum ,找到最大元素的 max 。 
此外还有在必要时再把它们转换回对象流的方法。

映射到数值流

将流转化为特化版本的常用方法为 mapToInt(),mapToDouble(),mapToLong()。

这些方法和前面说的 map 方法的工作方式一样,只是它们返回的是一个特化流而不是 Stream
 

        // 第二种方式:工资前3员工的薪水总和计算,使用特化流
        int sum = employees.stream()
                .sorted(Comparator.comparing(Employee::getSalary).reversed())
                .limit(3)
                .mapToInt(Employee::getSalary)// 返回一个IntStream
                .sum();
        System.out.println("工资前3员工的薪水总和计算:"+sum);

演示结果:

请注意,如果流是空的, sum 默认返回 0 。 IntStream 还支持其他的方便方法,如max 、 min 、 average 等。

转换回对象流 boxed()

同样,一旦有了数值流,你可能会想把它转换回非特化流。

        // 特化流IntStream
        IntStream intStream = employees.stream().mapToInt(Employee::getSalary);
        // 转换回常规流Stream
        Stream stream = intStream.boxed();

默认值 OptionalInt

要找到 IntStream 中的最大元素,可以调用 max 方法,它会返回一个 OptionalInt,如果没有最大值的话,可以显式处理OptionalInt 去定义一个默认值,演示如下:

        // 求最大薪水
        OptionalInt optionalInt = employees.stream().mapToInt(Employee::getSalary).max();
        // 显示设置默认值
        int maxValue = optionalInt.orElse(0);
        System.out.println("最大薪水:"+ maxValue);

数值范围

和数字打交道时,有一个常用的东西就是数值范围。比如,假设你想要生成1和10之间的所有数字。

java8引入了两个可用于IntStream和LongStream的静态方法,range 和 rangeclosed。

这两个方法都是第一个参数接受起始值,第二个参数接受结束值,但range是不包含结束值的,rangeclosed包含结束值

        // range()求0-10内的偶数
        List rangeList = IntStream.range(1, 10)
                .filter(x -> x % 2 == 0)
                .boxed() // 将特化流转化为常规流
                .collect(toList());
        System.out.println("range()求0-10内的偶数:"+rangeList);

        // rangeClosedList()求0-10内的偶数
        List rangeClosedList = IntStream.rangeClosed(1, 10)
                .filter(x -> x % 2 == 0)
                .boxed() // 将特化流转化为常规流
                .collect(toList());
        System.out.println("rangeClosedList()求0-10内的偶数:"+rangeClosedList);

演示结果:

3、综合练习代码示例

(1)综合练习一

public class EmployeeDemo {
    public static void main(String[] args) {
        List employees = Arrays.asList(
                new Employee("Matt", 5000, "New York"),
                new Employee("Steve", 6000, "London"),
                new Employee("Carrie", 10000, "New York"),
                new Employee("Peter", 7000, "New York"),
                new Employee("Alec", 6000, "London"),
                new Employee("Sarah", 8000, "London"),
                new Employee("Rebecca", 4000, "New York"),
                new Employee("Pat", 20000, "New York"),
                new Employee("Tammy", 9000, "New York"),
                new Employee("Fred", 15000, "Tokyo"));

        // List转Map:collect为终结操作
        Map employeeMap = employees.stream()
                .collect(Collectors.toMap(Employee::getName, Employee::getOffice));
        // 遍历map:需要设置两个参数,key(x),value(y)
        employeeMap.forEach((x,y)->System.out.println("key:"+x+","+"value:"+y));

        // Map的过滤:筛选值为New York的map
        Map new_yorkMap = employeeMap.entrySet().stream()
                .filter(x -> x.getValue().equals("New York"))
                .collect(Collectors.toMap(x -> x.getKey(), x -> x.getValue()));
        
        // 分组操作:groupingBy,通过办公地点进行分组
        Map> emloyeeGroup = employees.stream()
                .collect(Collectors.groupingBy(Employee::getOffice));
        System.out.println(emloyeeGroup);
        /**
         * 分组结果Map>:
         * {New York = [lambada.Employee@2ef1e4fa, lambada.Employee@306a30c7,
         *             lambada.Employee@b81eda8, lambada.Employee@68de145,
         *             lambada.Employee@27fa135a, lambada.Employee@46f7f36a],
         * Tokyo = [lambada.Employee@421faab1],
         * London = [lambada.Employee@2b71fc7e, 
         *           lambada.Employee@5ce65a89, 
         *           lambada.Employee@25f38edc]}
         */

        // 求最大值:返回最高工资
        Optional max = employees.stream()
                .max((e1, e2) -> Integer.compare(e1.getSalary(), e2.getSalary()));
        System.out.println(max.get().getSalary());// 20000

        // 去重:返回城市列表distinct()
        List cityList = employees.stream()
                .map(Employee::getOffice)
                .distinct()
                .collect(Collectors.toList());

        // 排序:默认是升序
        List sortList = employees.stream()
                .sorted(Comparator.comparing(Employee::getSalary))
                .collect(Collectors.toList());
        sortList.forEach(x->System.out.print(x.getSalary()+"|")); 
        // 打印结果:4000|5000|6000|6000|7000|8000|9000|10000|15000|20000|
        // 另一种方式
        employees.sort(Comparator.comparing(Employee::getSalary).reversed());
        // 降序排序获(reversed())取最高工资前两个
        List sortBList = employees.stream()
                .sorted(Comparator.comparing(Employee::getSalary).reversed())
                .limit(2)
                .collect(Collectors.toList());

        // 求平均值:获取指定工作地点的平均工资
        OptionalDouble new_york = employees.stream()
                .filter(x -> x.getOffice().equals("New York"))
                .mapToInt(Employee::getSalary)
                .average();
        System.out.println(new_york.getAsDouble());
    }
}

(2)综合练习二

public class TraderDemo {
    public static void main(String[] args) {

        Trader raoul = new Trader("Raoul", "Cambridge");
        Trader mario = new Trader("Mario", "Milan");
        Trader alan = new Trader("Alan", "Cambridge");
        Trader brian = new Trader("Brian", "Cambridge");

        List transactions = Arrays.asList(
                new Transaction(brian, 2011, 300),
                new Transaction(raoul, 2012, 1000),
                new Transaction(raoul, 2011, 400),
                new Transaction(mario, 2012, 710),
                new Transaction(mario, 2012, 700),
                new Transaction(alan, 2012, 950)
        );

        // (1) 找出2011年发生的所有交易,并按交易额排序(从低到高)。
        List list1 = transactions.stream()
                .filter(x -> x.getYear() == 2011)
                .sorted(Comparator.comparing(Transaction::getValue))
                .collect(toList());
        // list1.forEach(x-> System.out.println(x.getValue()));

        // (2) 交易员都在哪些不同的城市工作过?
        /**
         * 方式一
         */
        List list2_1 = transactions.stream()
                .map(Transaction::getTrader)
                .map(Trader::getCity)
                .distinct()
                .collect(toList());
        /**
         * 方式二
         */
        List list2_2 = transactions.stream()
                .map(x -> x.getTrader().getCity())
                .distinct()
                .collect(toList());

        // (3) 查找所有来自于剑桥的交易员,并按姓名排序。
        List list3 = transactions.stream()
                .filter(x -> x.getTrader().getCity().equals("Cambridge"))
                .map(Transaction::getTrader)
                .sorted(Comparator.comparing(Trader::getName))
                .collect(toList());
        // 筛选后有四个值,按照名字进行去重打印
        // list3.stream().map(Trader::getName).distinct().forEach(System.out::println);

        // (4) 返回所有交易员的姓名字符串,按字母顺序排序。
        /**
         * 第一种方式
         */
        List list4_1 = transactions.stream()
                .map(Transaction::getTrader)
                .sorted(Comparator.comparing(Trader::getName))
                .collect(toList());
        // list4_1.stream().map(Trader::getName).distinct().forEach(System.out::println);

        /**
         * 第二种方式
         */
        List list4_2 = transactions.stream()
                .map(Transaction::getTrader)
                .map(Trader::getName)
                .distinct()
                .sorted()
                .collect(toList());

        // (5) 有没有交易员是在米兰工作的?
        boolean milan = transactions.stream()
                .map(Transaction::getTrader)
                .map(Trader::getCity)
                .anyMatch(x -> x.equals("Milan"));

        // (6) 打印生活在剑桥的交易员的所有交易额。
        List cambridgeList = transactions.stream()
                .filter(x -> x.getTrader().getCity().equals("Cambridge"))
                .map(Transaction::getValue)
                .collect(toList());

        // (7) 所有交易中,最高的交易额是多少?
        /**
         * 方式1,使用归约
         */
        Optional reduce = transactions.stream()
                .map(Transaction::getValue)
                .reduce(Integer::max);
        /**
         * 方式2,使用数值流
         */
        OptionalInt max = transactions.stream()
                .mapToInt(Transaction::getValue)
                .max();

        // (8) 找到交易额最小的交易。
        OptionalInt min = transactions.stream()
                .mapToInt(Transaction::getValue)
                .min();

        // (9) 计算交易总额
        /**
         * 方式一:使用数值流,更简洁、高效
         */
        int sum1 = transactions.stream()
                .mapToInt(Transaction::getValue)
                .sum(); // 4060
        /**
         * 方式二:归约方式
         */
        Optional sum2 = transactions.stream()
                .map(Transaction::getValue)
                .reduce((x, y) -> x + y); // Optional[4060]
    }
}

你可能感兴趣的:(Java基础)