函数式编程-高级集合类和收集器

96 阅读3分钟

1.方法引用

Java 8提供了重写Lambda的简单语法:方法引用

方法引用的基本格式:Classname.Methodname,注意这里仅仅是提供一种和Lambda等价的结构,并不表示方法的调用,即不需要加括号。下面是方法引用的示例:

Student::getName

等价于

student -> student.getName()

方法引用支持多个参数


  public BiConsumer<String,Integer> fun = Student::new;// 支持多参数的方法引用
    
  class Student{
      private String name;
      private Integer age;

      public Student(String name, Integer age) {
          this.name = name;
          this.age = age;
      }
  }

2.元素顺序

直观上来看流是有顺序的,因为流都是按照出现顺序来依次处理的,但是出现顺序的取决于与产生数据流的数据源(是有序集还是无无序集)以及对流的一些操作(如sorted()排序操作会改变接下来流产生数据的顺序)。

有序的流和无序的流对于不同操作的性能开销是不同的,可以调用**unordered()**将有序流变为无序流,进行后续的处理,但是一般来说多数操作如reduce、filter、map都是在有序流上的效率更高。

另外需要注意的是,在使用并行流时,forEach方法不能保证元素是顺序处理的,而应当使用forEachOrdered

3.使用收集器

在流式编程中,我们往往面临着在对数据处理后的收集保存需求。在java.util.stream.Collectors标准类库为我们提供了收集器来实现这一需求。

3.1 将结果收集到其他类型的集合中

我们比较常用的写法是.collect(Collectors.toList()),这里我们不需要关心收集的List的具体实现,较为便捷. 但是在一些特定的情况下,我们必须关注,比如在串行处理下要求结果集中数据有序

stream().collect(Collectors.toCollection(TreeSet::new));

在比如我们收集并发流处理的Set,就必须要求是具备线程安全的Set实现类.

stream().collect(Collectors.toCollection(CopyOnWriteArraySet::new));

3.2 将结果收集后转化为值

3.2.1 转化为String

Collectors.joinin可以方便收集流中的值得到一个字符串,同时该方法允许使用自定义元素之间的分隔符、结果字符串前缀和结果字符串后缀.

String[] array = new String[]{"1","2","3","4"};
//1234
System.out.println(Arrays.stream(array).collect(Collectors.joining()));

//1|2|3|4
System.out.println(Arrays.stream(array).collect(Collectors.joining("|")));

//[1|2|3|4]
System.out.println(Arrays.stream(array).collect(Collectors.joining("|","[","]")));

3.2.1 转化为数值

除了将流中的元素收集为一个String之外还可以将流终中的元素收(计)集(算)为一个数值. 计算最大值Collectors.maxBy和最小值Collectors.maxBy

	Integer[] array = new Integer[]{0,2,3,4};
	Integer res = Arrays.stream(array).collect(Collectors.maxBy(Integer::compareTo)).get();
	System.out.println(res);
	// 结果:4

⚠️这里使用的Integer::compareTo而非Integer::max

计算均值Collectors.averagingInt

        Integer[] array = new Integer[]{1,2,3,4};
        Double res = Arrays.stream(array).collect(Collectors.averagingInt(i -> i + 1));
        System.out.println(res);
        // 结果: 3.5 

注意averagingInt方法名中int指的是对Int类型的数据进行计算,计算的结果值是一个Double值

3.3 数据分块/分组

我们已经很熟悉SQL中group by语句,而在函数式编程中Stream类库中也有与之对应的实现:Collectors.groupingBy

按照对3取模的情况进行分组

	Integer[] array = new Integer[]{1,2,3,4};
        Map<Integer, List<Integer>> res = Arrays.stream(array).collect(Collectors.groupingBy(i -> i % 3));
        System.out.println(res);
        //结果:{0=[3], 1=[1, 4], 2=[2]}

若是分组的情况只有两类,可以使用Collectors.partitioningBy

        Integer[] array = new Integer[]{1,2,3,4};
        Map<Boolean, List<Integer>> res = Arrays.stream(array).collect(Collectors.partitioningBy(i -> i % 2 == 0));
        System.out.println(res);
        //结果: {false=[1, 3], true=[2, 4]}

两者区别:

  • Collectors.groupingBy入参为Function<T,R> 返回值R即为Map的Key;
  • Collectors.partitioningBy 入参为Predicate ,Map的Key为true or false;入参为Predicate入参T为Valu.

3.4 组合收集器

3.5 重构和定制收集器

3.6 对收集器的归一化处理

4.其余的一些细节