JAVA-Stream流式编程总结

443 阅读5分钟

生成流

 在Java8中,Collection-API提供了两个方法生成流:

  • stream() − 为集合创建串行流
  • parallelStream() − 为集合创建并行流
 //串行流
 1.Stream stream = Lists.newArrayList(1,2,3).stream();
 //并行流
 2.Stream<Integer> integerStream = Lists.newArrayList(1, 2, 3).parallelStream();
 //这种方式生成的是串行流
 3.Stream<Integer> integerStream1 = Stream.of(1, 2, 3);
 //这种方式生成的也是串行流
 4.Stream<Object> build = Stream.builder().add(1).add(2).add(3).build();
 //生成自己的流,'::'是方法的引用,Supplier是一个函数接口,这两行代码就是生成一个有三个随机整型元素的串行流
 5.Supplier<Integer> supplier = new Random()::nextInt;
   Stream<Integer> generate = Stream.generate(supplier).limit(3);

 我们还可以通过自定义Supplier接口来实现一个串行流

//自定义Supplier接口
private static class MySupplier implements Supplier<Book> {

        private int index;

        @Override
        public Book get() {
            return new Book("Supplier" + index, new Random().nextInt(1000), index++);
        }
    }
@Data
private static class Book {
    private String name;
    private Integer pages;
    private Integer index;
};
public static void main(String[] args) {
      Stream<Book> limit = Stream.generate(new MySupplier()).limit(3);
      limit.forEach(System.out::println);
}
//输出结果
Book{name='Supplier1', pages=932, index=0}
Book{name='Supplier2', pages=183, index=1}
Book{name='Supplier3', pages=201, index=2}

列表类型基本操作

 基本操作包括循环、过滤、排序、统计、合并、去重等

  • 循环 打印list中的每一个元素,stream()可以省略
Lists.newArrayList(1, 2, 3).stream().forEach(System.out::println);
  • 过滤 保留list中大于2的元素并打印,filter中是一个Predicate的函数接口,这里也可以自己实现。同理这里integer>2 表达式可以替换为返回值为布尔类型的任意方法,true保留元素。
Lists.newArrayList(1, 2, 3).stream().filter(integer -> integer > 2).forEach(System.out::println);
  • 排序 默认为按自然顺序排序 则输出为 2,6,9,11,33
  • sorted()方法可以自定义一个实现了Comparator接口的类,实现自定义排序
Lists.newArrayList(11, 2, 33,6,9).stream().sorted();
Stream.generate(new MySupplier()).limit(3).sorted(new Comparator<Book>() {
            @Override
            public int compare(Book o1, Book o2) {
                return o1.getPages() - o2.getPages();
            }
        });
可以简化为:Stream.generate(new MySupplier()).limit(3).sorted((o1, o2) -> o1.getPages() - o2.getPages());
可以最终简化为:Stream.generate(new MySupplier()).limit(3).sorted(Comparator.comparingInt(Book::getPages));
//输出结果
Book{name='Supplier', pages=4, index=2}
Book{name='Supplier', pages=28, index=0}
Book{name='Supplier', pages=883, index=1}
  • 统计
  ArrayList<Integer> numbers = Lists.newArrayList(11, 2, 33, 6, 9);
  IntSummaryStatistics intSummaryStatistics = numbers.stream().mapToInt(x -> x).summaryStatistics();
  System.out.println("列表中最大的数 : " + intSummaryStatistics.getMax());
  System.out.println("列表中最小的数 : " + intSummaryStatistics.getMin());
  System.out.println("所有数之和 : " + intSummaryStatistics.getSum());
  System.out.println("平均数 : " + intSummaryStatistics.getAverage());
  • 合并list流  Stream的静态方法Stream.concat(stream1,stream2)可以将两个流合成一个 Stream的静态方法Stream.of(stream1,stream2,stream3...stream)可以将n个stream组成一个list,在用flatMap方法将list中每个stream合为一个
  ArrayList<Integer> list1 = Lists.newArrayList(1, 2, 3, 4);
  ArrayList<Integer> list2 = Lists.newArrayList(5, 6, 7, 8);
  ArrayList<Integer> list3 = Lists.newArrayList(9, 10, 11, 12);
  List<Integer> concat = Stream.concat(list1.stream(), list2.stream()).collect(Collectors.toList());
  System.out.println(StringUtils.join(concat,","));
  List<Integer> collect = Stream.of(list1.stream(),list2.stream(),list3.stream()).flatMap(item -> item).collect(Collectors.toList());
  System.out.println(StringUtils.join(collect,","));
  //输出
  1,2,3,4,5,6,7,8
  1,2,3,4,5,6,7,8,9,10,11,12
  • 去重操作 对于基础类型的数据直接用Stream的distinct()方法即可去重返回一个新的流
    对于自定义对象去重则需要重写自定义对象的equals()方法
ArrayList<Integer> distinct = Lists.newArrayList(1,1,2,2,2,3,4,5,5);
List<Integer> distinctList = distinct.stream().distinct().collect(Collectors.toList());
System.out.println(StringUtils.join(distinctList,","));
  • reduce操作 reduce是一个聚合操作,入参是BinaryOperator函数接口,有一个默认apply方法,取两个值并产生一个新值。意思就是将一个流的所有元素按照这个聚合函数聚合成一个结果
@FunctionalInterface
public interface BiFunction<T, U, R> {
    R apply(T t, U u);
}
//例如对一个list求和
ArrayList<Integer> reduce = Lists.newArrayList(1,1,2,2,2,3,4,5,5);
Integer reduceResult = reduce.stream().reduce(Integer::sum).orElse(0);
  • skip/limit操作 limit返回stream的前面n个元素;
    skip则是跳过stream的n个元素;
    两者都返回新的流

2021-2-28

列表类型花式操作

  • List 转 Map<K,V>
List<Book> bookList = Lists.newArrayList(new Book("语文",10,1),new Book("数学",20,2),new Book("英语",30,3),new Book("英语",40,4));
LinkedHashMap<String, Book> bookMap = bookList.stream().collect(Collectors.toMap(Book::getName, books -> books, (old, news) -> old, LinkedHashMap::new));

Collectors的静态方法toMap可以将一个流输出为map格式的数据,这个方法有四个函数接口入参。

  • 第一个keyMapper是生成map的Key的方法,上面的例子是以Book类的name属性作为map的key
  • 第二个valueMapper是生成map的value方法,上面例子中是以整个Book对象为value作为map的value
  • 第三个mergeFunction是生成map过程中如果出现相同key时对应的策略方法,上面例子中是保留之前的key/value舍弃新的。
  • 第四个mapSupplier是返回值对应的map类型,上面例子是传入一个空的linkedHashMap,生成的结果会填充到这个map中,这里必须是map类型,因为静态方法声明时已经限定了泛型M需要继承Map
public static <T, K, U, M extends Map<K, U>>
    Collector<T, ?, M> toMap(Function<? super T, ? extends K> keyMapper,
                                Function<? super T, ? extends U> valueMapper,
                                BinaryOperator<U> mergeFunction,
                                Supplier<M> mapSupplier) {
        BiConsumer<M, T> accumulator
                = (map, element) -> map.merge(keyMapper.apply(element),
                                              valueMapper.apply(element), mergeFunction);
        return new CollectorImpl<>(mapSupplier, accumulator, mapMerger(mergeFunction), CH_ID);
    }
  • List分组
List<Book> bookList = Lists.newArrayList(new Book("语文",10,1),new Book("数学",25,2),new Book("英语",20,3),new Book("物理",40,4));
Map<String, List<Book>> bookMapByName = bookList.stream().collect(Collectors.groupingBy(Book::getName));

Collectors的静态方法groupingBy可以按某个属性对数据进行分组,类似与sql中的group by,这方法共有三个函数接口入

 public static <T, K, D, A, M extends Map<K, D>>
    Collector<T, ?, M> groupingBy(Function<? super T, ? extends K> classifier,
                                  Supplier<M> mapFactory,
                                  Collector<? super T, A, D> downstream) ;
  • 第一个classifier函数是将输入作为分组的key,上面例子中是以book的name属性最为分组的key。
  • 第二个mapFactory函数是将限定输出的map类型,上面例子中是默认的HashMap。
  • 第三个downstream函数是进行下游的操作,这个操作可以嵌套,上面例子中是默认的生成一个list
1.按index进行分组,然后输出书名
List<Book> bookList = Lists.newArrayList(new Book("语文",10,1),new Book("数学",21,2),new Book("英语",30,2),new Book("物理",40,4));
Map<Integer, List<String>> collect2 = bookList.stream().collect(Collectors.groupingBy(Book::getIndex, Collectors.mapping(Book::getName, Collectors.toList())));

2.按页数进行二分组,大于20页的一组,然后输出书名
List<Book> bookList = Lists.newArrayList(new Book("语文",10,1),new Book("数学",21,2),new Book("英语",30,3),new Book("物理",40,4));
Map<Boolean, List<String>> collect1 = bookList.stream().collect(Collectors.partitioningBy(item -> item.getPages() > 20, Collectors.mapping(Book::getName, Collectors.toList())));
//输出结果
{false=[语文], true=[数学, 英语, 物理]}