生成流
在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=[数学, 英语, 物理]}