lambda - Stream

一、lambda写法

public class LambdaDemo1{
    
    // 定义接口
    interface Printer{
        void printer(String val);
    }
    
    // 定义一个打印方方法
    public void printSomething(String something,Printer printer){
        printer.printer(something);
    }
    
    public static void main(String[] args){
        LambdaDemo1 lambdaDemo1 = new LambdaDemo1();
        String something = "something";
        
        // 传统写法
        Printer printer = new Printer(){
            @Override
            public void printer(String val){
                System.out.println("传统写法:"val);
            }
        };
        printSomething(something,printer);
        
        // lambda表达式 - 写法1
        // lambda表达式用 -> 连接参数和方法体
        // (String val) 参数 -- 左侧
        // {} 方法体 -- 右侧
        Printer printer = (String val) -> {
             System.out.println("传统写法:"val);
        };
        printSomething(something,printer);
        
        // lambda表达式 - 写法2
        // 简写“写法1” -- 省略参数类型。
        // lambda可根据定义的接口和上下文自动识别参数的类型
        // 如果参数与类型不匹配会报错
        Printer printer = (val) -> {
             System.out.println("传统写法:"val);
        };
        printSomething(something,printer);
        
        // lambda表达式 - 写法3
        // 简写“写法2”
        // 省略参数小括号:
        //		如果只有一个参数,可省略参数外层的()
        // 省略方法体花括号
        // 		如果方法体中只有一行代码,可省略方法体的{}
        Printer printer = val -> System.out.println("传统写法:"val);
        printSomething(something,printer);
      
        // lambda表达式 - 写法4
        // 简写“写法3”
        // printSomething中的参数printer
        // Printer printer = val -> System.out.println("传统写法:"val)
        printSomething(something,val -> System.out.println("传统写法:"val));
    }
}

二、Stream

1、Stream源操作

// 1、集合转Stream
List<String> nameStrs = Arrays.asList("Monkey","Lion","Giraffe","Lemur");

List<String> sorted  = nameStrs.stream() // 
    		.filter(s -> s.startsWith("L"))	// 过滤。判断条件:以字母"L"开头
    		.map(String::toUpperCase)		// 将过滤到的元素,全部变成大写
    		.sorted()	// 排序
    		.collect(toList());	// 组合成一个集合
// 2、数组转Stream
String[] players = {"kobe","james","curry","cyyt"};

Stream.of(players)
      .filter(s -> s.startsWith("L"))
      .map(String::toUpperCase)
      .sorted()
      .collect(toList());	
// 3、HashSet转Stream
String[] players = {"kobe","james","curry","cyyt"};
Set<String> set = new HashSet<>(players);

List<String> sorted  = set.stream() // 
    		.filter(s -> s.startsWith("L"))	// 过滤。判断条件:以字母"L"开头
    		.map(String::toUpperCase)		// 将过滤到的元素,全部变成大写
    		.sorted()	// 排序
    		.collect(toList());	// 组合成一个集合
// 4、行文本文件转Stream
Files.lines(Paths.get("file.txt"))
     .filter(s -> s.startsWith("L"))	// 过滤。判断条件:以字母"L"开头
     .map(String::toUpperCase)		// 将过滤到的元素,全部变成大写
     .sorted()	// 排序
     .collect(toList());	// 组合成一个集合

2、Stream中间操作

2.1 无状态操作

2.1.1、filter(过滤数据)
public class Employee {
    private Integer id;
    private Integer age;
    private String gender;
    private String firstName;
    private String lastName;
    
    // 省略get set 构造
}

public class StreamFilterPredicate {
    
    public static void main(String[] args){
        Employee e1 = new Employee(1,20,"男","First1","Last1");
        Employee e2 = new Employee(2,21,"女","First2","Last2");
        Employee e3 = new Employee(3,24,"男","First3","Last3");
        Employee e4 = new Employee(4,22,"男","First4","Last4");
        Employee e5 = new Employee(5,21,"女","First5","Last5");
        Employee e6 = new Employee(6,25,"男","First6","Last6");
        Employee e7 = new Employee(7,23,"女","First7","Last7");
        Employee e8 = new Employee(8,25,"男","First8","Last8");
        Employee e9 = new Employee(9,26,"女","First9","Last9");
        Employee e10 = new Employee(10,21,"男","First10","Last10");

        List<Employee> employees = Arrays.asList(e1,e2,e3,e4,e5,e6,e7,e8,e9,e10);

        List<Employee> list = employees.stream()
            // Predicate 谓语
            // e.getAge() > 22 && e.getGender().equals("女") 谓词逻辑
                 .filter(e -> {e.getAge() > 22 && e.getGender().equals("女")})
                 .collect(Collectors.toList());

        System.out.println(list);
    }
    
}

谓词逻辑也可抽出来,复用

public class Employee {
    private Integer id;
    private Integer age;
    private String gender;
    private String firstName;
    private String lastName;
    
    // 省略get set 构造
    
    public static Predicate<Employee> ageGreaterThan22 = x -> x.getAge() > 22;
    public static Predicate<Employee> genderWoman = x -> x.getGender().equals("女");
}

public class StreamFilterPredicate {
    
    public static void main(String[] args){
        Employee e1 = new Employee(1,20,"男","First1","Last1");
        Employee e2 = new Employee(2,21,"女","First2","Last2");
        Employee e3 = new Employee(3,24,"男","First3","Last3");
        Employee e4 = new Employee(4,22,"男","First4","Last4");
        Employee e5 = new Employee(5,21,"女","First5","Last5");
        Employee e6 = new Employee(6,25,"男","First6","Last6");
        Employee e7 = new Employee(7,23,"女","First7","Last7");
        Employee e8 = new Employee(8,25,"男","First8","Last8");
        Employee e9 = new Employee(9,26,"女","First9","Last9");
        Employee e10 = new Employee(10,21,"男","First10","Last10");

        List<Employee> employees = Arrays.asList(e1,e2,e3,e4,e5,e6,e7,e8,e9,e10);

        List<Employee> list = employees.stream()
                 // 支持
                 // .and():并且 
                 // .or():或者
                 // .negate():取反
                 .filter(Employee.ageGreaterThan22.and(Employee.genderWoman).negate())
                 .collect(Collectors.toList());

        System.out.println(list);
    }
    
}
2.1.2、map(转换数据)
  • 处理字符串类型集合元素
public class StreamMap1 {
    public static void main(String[] args){
        List<String> alpha = Arrays.asList("Monkey", "Lion", "Giraffe", "Lemur");

        //不使用Stream管道流
        List<String> alphaUpper = new ArrayList<>();
        for (String s : alpha) {
            alphaUpper.add(s.toUpperCase());
        }
        System.out.println(alphaUpper); //[MONKEY, LION, GIRAFFE, LEMUR]

        // 使用Stream管道流
        List<String> collect = alpha.stream().map(String::toUpperCase).collect(Collectors.toList());
        // "方法引用":String::toUpperCase。效果和s -> s.toUpperCase()效果一样
        //List<String> collect = alpha.stream().map(s -> s.toUpperCase()).collect(Collectors.toList());

        System.out.println(collect); //[MONKEY, LION, GIRAFFE, LEMUR]
    }
}
  • 处理非字符串类型集合元素
// map()函数不仅可以处理数据,还可以转换数据的类型。如下:
Stream.of("Monkey", "Lion", "Giraffe", "Lemur")
    .mapToInt(String::length)
    .forEach(System.out::println);

// 结果:
// 6
// 4
// 7
// 5

除了mapToInt。还有maoToLong,mapToDouble等等用法。
  • 处理对象数据格式转换
public class Employee {
    private Integer id;
    private Integer age;
    private String gender;
    private String firstName;
    private String lastName;
    
    // 省略get set 构造
}

public class StreamMap2 {
    public static void main(String[] args){
        
        Employee e1 = new Employee(1,23,"M","Rick","Beethovan");
        Employee e2 = new Employee(2,13,"F","Martina","Hengis");
        Employee e3 = new Employee(3,43,"M","Ricky","Martin");
        Employee e4 = new Employee(4,26,"M","Jon","Lowman");
        Employee e5 = new Employee(5,19,"F","Cristine","Maria");
        Employee e6 = new Employee(6,15,"M","David","Feezor");
        Employee e7 = new Employee(7,68,"F","Melissa","Roy");
        Employee e8 = new Employee(8,79,"M","Alex","Gussin");
        Employee e9 = new Employee(9,15,"F","Neetu","Singh");
        Employee e10 = new Employee(10,45,"M","Naveen","Jain");

        List<Employee> employees = Arrays.asList(e1, e2, e3, e4, e5, e6, e7, e8, e9, e10);
        
        // 将每一个Employee的年龄增加一岁
        // 将性别中的“M”换成“male”,F换成Female。
        List<Employee> maped = employees.stream()
            .map(e -> {
                e.setAge(e.getAge() + 1);
                e.setGender(e.getGender().equals("M")?"male":"female");
                return e;
            }).collect(Collectors.toList());
        
        
        List<Employee> peeked = employees.stream()
            .peek(e -> {
                e.setAge(e.getAge() + 1);
                e.setGender(e.getGender().equals("M")?"male":"female");
            }).collect(Collectors.toList());
        
        // .map()会return回一个新对象
        // .peek()是特殊的map函数。对同一个对象操作。实现效果一样。
        
        System.out.println(maped);
        System.out.println(peeked);
    }
}
2.1.3、flatMap(map展开)
// map可以对管道流中的数据进行转换操作,但是如果管道中还有管道,数组中还有数组该如何处理?
// 即:如何处理二维数组及二维集合类。
// 实现一个简单的需求:
// 		将“hello”,“world”两个字符串组成的集合,元素的每一个字母打印出来。
//		如果不用Stream我们怎么写?写2层for循环,第一层遍历字符串,并且将字符串拆分成char数组,第二层for循环遍历char数组。
public class StreamFlatMap {
    public static void main(String[] args){
        List<String> words = Arrays.asList("hello", "word");
        
        // .map():输出结果为:
        // 		java.util.stream.ReferencePipeline$Head@3551a94
		// 		java.util.stream.ReferencePipeline$Head@531be3c5
        // 证明:
        // 		用map方法是做不到的,这个需求用map方法无法实现。
        //		map只能针对一维数组进行操作,数组里面还有数组,管道里面还有管道,它是处理不了每一个元素的。
		words.stream()
        	.map(w -> Arrays.stream(w.split("")))
        	.forEach(System.out::println);
        
        // .flatMap():输出结果为:
        // h
		// e
		// l
		// l
		// o
		// w
		// o
		// r
		// d
        
        // flatMap可以理解为将若干个子管道中的数据全都,平面展开到父管道中进行处理。
        words.stream()
        .flatMap(w -> Arrays.stream(w.split("")))
        .forEach(System.out::println);
    }
}

2.2、有状态操作

2.2.1、distinct(去重)
// distinct方法时,调用Object的equals方法进行对象的比较。
// 如果你有自己的比较规则,可以重写equals方法。
// 经过管道处理之后的数据是: ["Monkey", "Lion", "Giraffe", "Lemur"]
List<String> uniqueAnimals = Stream.of("Monkey", "Lion", "Giraffe", "Lemur", "Lion")
        .distinct()
        .collect(Collectors.toList());
2.2.2、limit(取前N个)
// limt方法传入一个整数n,用于截取管道中的前n个元素。
// 经过管道处理之后的数据是:[Monkey, Lion]。
List<String> limitN = Stream.of("Monkey", "Lion", "Giraffe", "Lemur")
        .limit(2)
        .collect(Collectors.toList());
2.2.3、skip(跳过前N个)
// skip方法与limit方法的使用相反,用于跳过前n个元素,截取从n到末尾的元素。
// 经过管道处理之后的数据是: [Giraffe, Lemur]
List<String> skipN = Stream.of("Monkey", "Lion", "Giraffe", "Lemur")
        .skip(2)
        .collect(Collectors.toList());
2.2.4、sorted(排序)
  • 基本使用

    // 默认的情况下,sorted是按照字母的自然顺序进行排序。
    // 如下代码的排序结果是:[Giraffe, Lemur, Lion, Monkey]
    // 字数按顺序G在L前面,L在M前面。第一位无法区分顺序,就比较第二位字母。
    List<String> alphabeticOrder = Stream.of("Monkey", "Lion", "Giraffe", "Lemur")
            .sorted()
            .collect(Collectors.toList());
    
  • 像使用sql一样排序集合

    • 排序器

      // java8以前
      Collections.sort();
      
      // java8以后
      List<T> list = Arrays.asList();
      // Comparator<T> 排序器
      list.sort(Comparator<T>);
      
      // JDK默认定义的排序规则
      // 例如:
      // 大小写不敏感
      list.sort(String.CASE_INSENSITIVE_ORDER);
      // 自然顺序
      list.sort(Comparator.naturalOrder());
      
    • 对象排序

      public class Employee {
          private Integer id;
          private Integer age;
          private String gender;
          private String firstName;
          private String lastName;
          
          // 省略get set 构造 toString()
      }
      
      public class SortList {
          public static void main(String[] args){
              Employee e1 = new Employee(1,23,"M","Rick","Beethovan");
              Employee e2 = new Employee(2,13,"F","Martina","Hengis");
              Employee e3 = new Employee(3,43,"M","Ricky","Martin");
              Employee e4 = new Employee(4,26,"M","Jon","Lowman");
              Employee e5 = new Employee(5,19,"F","Cristine","Maria");
              Employee e6 = new Employee(6,15,"M","David","Feezor");
              Employee e7 = new Employee(7,68,"F","Melissa","Roy");
              Employee e8 = new Employee(8,79,"M","Alex","Gussin");
              Employee e9 = new Employee(9,15,"F","Neetu","Singh");
              Employee e10 = new Employee(10,45,"M","Naveen","Jain");
      
              List<Employee> employees = Arrays.asList(e1, e2, e3, e4, e5, e6, e7, e8, e9, e10);
              
              // 先是按性别的倒序排序,再按照年龄的倒序排序
              employees.sort(
                  Comparator.comparing(Employee::getGender)
                  .thenComparing(Employee::getAge)
                  .reversed()
      		);
              
              // 打印
              employees.forEach(System.out::println);
              
              // 与sql的区别:
              //		都是正序 ,不加reversed
              //		都是倒序,最后面加一个reserved
              //		先是倒序(加reserved),然后正序
              //		先是正序(加reserved,负负得正),然后倒序(加reserved)
      
          }
      }
      

3、Stream终端操作

1、ForEach和ForEachOrdered

// 如果我们只是希望将Stream管道流的处理结果打印出来,而不是进行类型转换。
// 我们就可以使用forEach()方法或forEachOrdered()方法。

Stream.of("Monkey", "Lion", "Giraffe", "Lemur", "Lion")
        .parallel()
        .forEach(System.out::println);

Stream.of("Monkey", "Lion", "Giraffe", "Lemur", "Lion")
        .parallel()
        .forEachOrdered(System.out::println); // 按顺序输出
  • parallel()函数表示对管道中的元素进行并行处理,而不是串行处理,这样处理速度更快。
    • 无法保证顺序: 可能导致管道流中后面的元素先处理,前面的元素后处理。
  • forEachOrdered
    • 虽然在数据处理顺序上可能无法保障,但是forEachOrdered方法可以在元素输出的顺序上保证与元素进入管道流的顺序一致。

2、collect(元素的收集)

2.1、收集为Set
// 通过Collectors.toSet()方法收集Stream的处理结果,将所有元素收集到Set集合中。

Set<String> collectToSet = Stream.of(
   "Monkey", "Lion", "Giraffe", "Lemur", "Lion"
) 
.collect(Collectors.toSet());

// 最终collectToSet 中的元素是:[Monkey, Lion, Giraffe, Lemur],注意Set会去重。
2.2、收集到List
// 同样,可以将元素收集到List使用toList()收集器中。

List<String> collectToList = Stream.of(
   "Monkey", "Lion", "Giraffe", "Lemur", "Lion"
).collect(Collectors.toList());

// 最终collectToList中的元素是: [Monkey, Lion, Giraffe, Lemur, Lion]
3.3、通用的收集方式
// 可以将数据元素收集到任意的Collection类型:即向所需Collection类型提供构造函数的方式。

LinkedList<String> collectToCollection = Stream.of(
   "Monkey", "Lion", "Giraffe", "Lemur", "Lion"
).collect(Collectors.toCollection(LinkedList::new));

//最终collectToCollection中的元素是: [Monkey, Lion, Giraffe, Lemur, Lion]

// 注意:以上代码中使用了LinkedList::new,实际是调用LinkedList的构造函数,将元素收集到Linked List。
// 当然你还可以使用诸如LinkedHashSet::new和PriorityQueue::new将数据元素收集为其他的集合类型,这样就比较通用了。
3.4、收集到Array
// 通过toArray(String[]::new)方法收集Stream的处理结果,将所有元素收集到字符串数组中。

String[] toArray = Stream.of(
   "Monkey", "Lion", "Giraffe", "Lemur", "Lion"
) .toArray(String[]::new);

//最终toArray字符串数组中的元素是: [Monkey, Lion, Giraffe, Lemur, Lion]
3.5、收集到Map
// Function.identity()方法,该方法很简单就是返回一个“ t -> t ”(输入就是输出的lambda表达式)
// distinct()来确保Map键值的唯一性

Map<String, Integer> toMap = Stream.of(
    "Monkey", "Lion", "Giraffe", "Lemur", "Lion"
)
.distinct()
.collect(Collectors.toMap(
       Function.identity(),   // 元素输入就是输出,作为key
       s -> (int) s.chars().distinct().count()// 元素的长度,作为value
));

// 最终toMap的结果是: {Monkey=6, Lion=4, Lemur=5, Giraffe=6}   
3.6、groupingBy(分组收集)
Map<Character, List<String>> groupingByList =  Stream.of(
    "Monkey", "Lion", "Giraffe", "Lemur", "Lion"
)
.collect(Collectors.groupingBy(
       s -> s.charAt(0) ,  // 根据元素首字母分组,相同的在一组
       // counting()       // 加上这一行代码可以实现分组统计
));

// 最终groupingByList内的元素: {G=[Giraffe], L=[Lion, Lemur, Lion], M=[Monkey]}
//如果加上counting() ,结果是:  {G=1, L=3, M=1}

// 这是以上代码过程的说明:groupingBy第一个参数作为分组条件,第二个参数是子收集器。
3.7、其他常用方法
// 判断管道中是否包含2,结果是: true
boolean containsTwo = IntStream.of(1, 2, 3).anyMatch(i -> i == 2);

// 统计元素个数:4
long nrOfAnimals = Stream.of(
    "Monkey", "Lion", "Giraffe", "Lemur"
).count();

// 管道中元素数据累加结果:sum: 6
int sum = IntStream.of(1, 2, 3).sum();

//管道中元素数据平均值:average: OptionalDouble[2.0]
OptionalDouble average = IntStream.of(1, 2, 3).average();

//管道中元素数据最大值:max: 3
int max = IntStream.of(1, 2, 3).max().orElse(0);

// 全面的统计结果statistics: IntSummaryStatistics{count=3, sum=6, min=1, average=2.000000, max=3}
IntSummaryStatistics statistics = IntStream.of(1, 2, 3).summaryStatistics();

4、Stream查找与匹配元素

4.1、anyMatch

public class Employee {
    private Integer id;
    private Integer age;
    private String gender;
    private String firstName;
    private String lastName;
    
    // 省略get set 构造 toString()
}

public class MatchFind {
    public static void main(String[] args){
        
        Employee e1 = new Employee(1,23,"M","Rick","Beethovan");
        Employee e2 = new Employee(2,13,"F","Martina","Hengis");
        Employee e3 = new Employee(3,43,"M","Ricky","Martin");
        Employee e4 = new Employee(4,26,"M","Jon","Lowman");
        Employee e5 = new Employee(5,19,"F","Cristine","Maria");
        Employee e6 = new Employee(6,15,"M","David","Feezor");
        Employee e7 = new Employee(7,68,"F","Melissa","Roy");
        Employee e8 = new Employee(8,79,"M","Alex","Gussin");
        Employee e9 = new Employee(9,15,"F","Neetu","Singh");
        Employee e10 = new Employee(10,45,"M","Naveen","Jain");

        List<Employee> employees = Arrays.asList(e1, e2, e3, e4, e5, e6, e7, e8, e9, e10);
        
        // 查找员工列表中是否包含年龄大于70的员工
        
        // 传统写法
        boolean isExistAgeThan70 = false;
        for(Employee employee:employees){
          if(employee.getAge() > 70){
            isExistAgeThan70 = true;
            break;
          }
        }
        System.out.println(isExistAgeThan70);
        
        // lambda写法
        // .anyMatch():判断Stream流中是否包含某一个“匹配规则”的元素
        // 只要包含一个“匹配规则”的元素,就返回true
        isExistAgeThan70 = employees.steam().anyMatch(e -> e.getAge() > 70);
    }
}

4.2、allMatch

// .allMatch():判断是够Stream流中的所有元素都符合某一个"匹配规则"
// 所有元素都符合某一个"匹配规则",才返回true
boolean isExistAgeThan10 = employees.stream().allMatch(e -> e.getAge() > 10);

4.3、noneMatch

// 判断是否Stream流中的所有元素都不符合某一个"匹配规则"
// 所有元素都不符合某一个"匹配规则",才返回true
boolean isExistAgeLess18 = employees.stream().noneMatch(e -> e.getAge() < 18);

4.4、Optional

// findFirst用于查找第一个符合“匹配规则”的元素,返回值为Optional
// findAny用于查找任意一个符合“匹配规则”的元素,返回值为Optional

// 从列表中按照顺序查找第一个年龄大于40的员工。
Optional<Employee> employeeOptional
        =  employees.stream().filter(e -> e.getAge() > 40).findFirst();
System.out.println(employeeOptional.get());

// Optional类代表一个值存在或者不存在。在java8中引入,这样就不用返回null了。
// 		如果存在,调用get()方法获取。
//		如果不存在,调用get()方法,抛出异常。

  • get()。如果存在,获取值。如果不存在,抛出异常。

  • isPresent() 方法。存在返回true,不存在返回false

    boolean is =  employees.stream().filter(e -> e.getAge() > 40).findFirst().isPresent();
    System.out.println(is);
    
  • ifPresent(Consumer block)。会在值存在的时候执行给定的代码块。

    employees.stream().filter(e -> e.getAge() > 40).findFirst().ifPresent(e -> System.out.println(e));
    
  • orElse。如果不存在值,给一个默认值

    employees.stream().filter(e -> e.getAge() > 40).findFirst()
        .orElse(new Employee(0,0,"F","",""));
    

5、Stream集合元素归约

Stream.reduce,用来实现集合元素的归约.

reduce函数有三个参数:

  • Identity标识:一个元素,它是归约操作的初始值,如果流为空,则为默认结果。
  • Accumulator累加器:具有两个参数的函数:归约运算的部分结果和流的下一个元素。
    • 阶段累加结果作为累加器的第一个参数
    • 集合遍历元素作为累加器的第二个参数
  • Combiner合并器(可选):当归约并行化时,或当累加器参数的类型与累加器实现的类型不匹配时,用于合并归约操作的部分结果的函数。

5.1、Integer类型规约

// reduce初始值为0,累加器可以是lambda表达式,也可以是方法引用。

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6);

Integer total = numbers
        .stream()
        .reduce(0, (subtotal, element) -> subtotal + element);
System.out.println(total);  //21

Integer result = numbers
        .stream()
        .reduce(0, Integer::sum);
System.out.println(result); //21

5.2、String类型归约

// 不仅可以归约Integer类型,只要累加器参数类型能够匹配,可以对任何类型的集合进行归约计算。

List<String> letters = Arrays.asList("a", "b", "c", "d", "e");

String res = letters
        .stream()
        .reduce("", (partialString, element) -> partialString + element);
System.out.println(res);  // abcde


String result = letters
        .stream()
        .reduce("", String::concat);
System.out.println(result);  // abcde

5.3、复杂对象归约

Employee e1 = new Employee(1,23,"M","Rick","Beethovan");
Employee e2 = new Employee(2,13,"F","Martina","Hengis");
Employee e3 = new Employee(3,43,"M","Ricky","Martin");
Employee e4 = new Employee(4,26,"M","Jon","Lowman");
Employee e5 = new Employee(5,19,"F","Cristine","Maria");
Employee e6 = new Employee(6,15,"M","David","Feezor");
Employee e7 = new Employee(7,68,"F","Melissa","Roy");
Employee e8 = new Employee(8,79,"M","Alex","Gussin");
Employee e9 = new Employee(9,15,"F","Neetu","Singh");
Employee e10 = new Employee(10,45,"M","Naveen","Jain");

List<Employee> employees = Arrays.asList(e1, e2, e3, e4, e5, e6, e7, e8, e9, e10);

// 计算每个员工的年龄之和
Integer total = employees.stream()
    // 先用map将Stream流中的元素由Employee类型处理为Integer类型(age)。
    .map(Employee::getAge)
    // 然后对Stream流中的Integer类型进行归约
    .reduce(0,Integer::sum);
System.out.println(total); //346

5.4、Combiner合并器的使用

// 在进行并行流计算的时候,可能会将集合元素分成多个组计算。
// 为了更快的将分组计算结果累加,可以使用合并器。

Integer total2 = employees
    // 并行流
        .parallelStream()
    // 获取每个员工年龄
        .map(Employee::getAge)
    // 对Stream流中的Integer类型进行归约
        .reduce(0,Integer::sum,Integer::sum);  //注意这里reduce方法有三个参数

System.out.println(total); //346
// 因为Stream流中的元素是Employee,累加器的返回值是Integer,所以二者的类型不匹配。
// 这种情况下可以使用Combiner合并器对累加器的结果进行二次归约,相当于做了类型转换。

Integer total3 = employees.stream()
        .reduce(0,(totalAge,emp) -> totalAge + emp.getAge(),Integer::sum); //注意这里reduce方法有三个参数
System.out.println(total); //346

三、函数式接口

1、特点

  • 接口有且仅有一个抽象方法
  • 允许定义静态非抽象方法
  • 允许定义默认defalut非抽象方法(default方法也是java8才有的)
    • 在java8之前,如果一个接口存在多个实现类。如果接口中添加新方法,实现类都要去修改一遍,继承该方法。
    • 在java8中,用defalut关键字定义一个方法,其接口的实现类就不需要继承该方法,使用时调用即可。
    • defalut关键字,还可以在函数式接口中定义非抽象方法,既有方法体。
  • 允许java.lang.Object中的public方法
  • @FunctionInterface注解不是必须的,如果一个接口符合"函数式接口"定义,那么加不加该注解都没有影响。
    • 加上该注解能够更好地让编译器进行检查。
    • 如果编写的不是函数式接口,但是加上了@FunctionInterface,那么编译器会报错。

2、自定义Comparator排序

// 传统写法
employees.sort(new Comparator<Employee>() {
    @Override
    public int compare(Employee em1, Employee em2) {
        if(em1.getAge() == em2.getAge()){
            return 0;
        }
        return em1.getAge() - em2.getAge() > 0 ? -1:1;
    }
});
employees.forEach(System.out::println);

// lambda写法
employees.sort((em1,em2) -> {
    if(em1.getAge() == em2.getAge()){
        return 0;
    }
    return em1.getAge() - em2.getAge() > 0 ? -1:1;
});
employees.forEach(System.out::println);

本篇文章学习自 字母哥-《恕我直言:你可能真的不会JAVA系列》。

上一篇:如何在Android手机上启用USB网络共享到Mac OS X Lion


下一篇:Gradle入门详解