Java------Stream流式编程高级API【mapTo、Collectors、groupingBy、flatMap】(五)

mapTo,折叠操作

折叠操作又称为规约操作,是从一系列输入元素中,通过组合操作组成单个摘要结果。比如:查找一组数字的和、最大值、最小值、个数等。
常用的折叠操作:
max()、min()、count()、sum()、groupby()、groupby()+max()。
以上为了方便对纯数字的流进行处理。
提供了三种基本类型的Stream
IntStream、DoubleStream、LongStream。
通过Stream接口中的三个方法可以得到这三个对象。

IntStream mapToInt(ToIntFunction<? super T> mapper);
//ToIntFunction函数式接口,放lamba表达式
@FunctionalInterface
public interface ToIntFunction<T> {

    /**
     * Applies this function to the given argument.
     *
     * @param value the function argument
     * @return the function result
     */
    int applyAsInt(T value);
}
LongStream mapToLong(ToLongFunction<? super T> mapper);
DoubleStream mapToDouble(ToDoubleFunction<? super T> mapper);

示例一:得到数字流,求最大、最小

@Test
    public void test1() {
        int[] intArr = new int[]{1,2,3,4,5,1,2};
        IntStream stream = Arrays.stream(intArr);
        //求个数
        long count = stream.count();
        System.out.println(count);
        //求最大值
        OptionalInt max = Arrays.stream(intArr).max();
//        int asInt = stream.max().getAsInt();
        //直接输出max会报错,
        System.out.println(max);
//        System.out.println(asInt);
        //求最小值 会报错, stream has already been operated upon or closed
        OptionalInt min = stream.min();
        System.out.println(min);
    }

其中调用max值时,返回的类型是OptionalInt,打印输出的结果时:OptionalInt[5],如果想获取它的value。则需要调用getAsInt()方法。

OptionalInt max = Arrays.stream(intArr).max();

根据源码:如果没有值的话,会抛出异常,有值,则返回value

public int getAsInt() {
        if (!isPresent) {
            throw new NoSuchElementException("No value present");
        }
        return value;
    }

而流直接调用max方法,也会抛出异常,stream has already been operated upon or closed。此时需要新建一个流

OptionalInt min = stream.min();
Arrays.stream(intArr).min();

案例二:List数组、map进行数字流处理

@Test
    public void test1() {
        Map<String,Integer> map = new HashMap<>();
        map.put("a",1);
        map.put("b",2);
        map.put("c",3);
        map.put("d",4);
        map.put("e",5);
        Stream<Map<String, Integer>> map1 = Stream.of(map);
        long a = map1.mapToInt(x -> x.get("a")).count();
        //1
        System.out.println(a);

        List<Studengt> list = new ArrayList<>();
        list.add(new Studengt("aa",1));
        list.add(new Studengt("bb",2));
        list.add(new Studengt("cc",9));
        OptionalInt max = list.stream().mapToInt(x -> x.getAge()).max();
        //9
        System.out.println(max.getAsInt());
    }

Collectors 实现将流转list、set、map

使用Collectors收集器,实现对流进行可变的折叠操作。将流转成List、Set、Map。
在Stream接口中有一个collect方法收集器进行操作
collect源码

<R, A> R collect(Collector<? super T, A, R> collector);

Collector源码

public interface Collector<T, A, R> {

如何得到收集器对象?在收集器中制定了转换list、map、set的方法。
在collectors中定义了很多收集器创建方法,toList实现转list、toSet转set,toMap转map。
1.创建流对象
2.创建收集器对象
3.执行stream流对象的collection方法,进行收集,转成List、set、Map。

案例三:stream转List。

String[] strArr = new String[]{"a","bb","ccc","dddd"};
        //默认返回arrylist
        List<String> collect = Arrays.stream(strArr).collect(Collectors.toList());
        collect.stream().forEach(System.out::println);
        //指定linkList,LinkedList::new构造方法引用
        LinkedList<String> collect1 = Arrays.stream(strArr).collect(Collectors.toCollection(LinkedList::new));

案例四:基本类型数组转流对象

int数组调用stream方法,返回的时IntStream对象,这样的对象时无法调用。

int[] intArr = new int[]{1,22,333,4444,55555};
        //语句报错无法执行
        Arrays.stream(intArr).collect(Collectors.toList());

此时调用的collect方法时,另一个需要传入三个参数的方法。

<R> R collect(Supplier<R> supplier,
                  ObjIntConsumer<R> accumulator,
                  BiConsumer<R, R> combiner);

如果想要正常转List,则需要调用.stream().boxed()方法。
.boxed():将流中的元素进行装箱,将基本类型的数组转成含有包装类的流。

如代码所示

int[] intArr = new int[]{1,22,333,4444,55555};
        //语句报错无法执行
        List<Integer> collect1 = Arrays.stream(intArr).boxed().collect(Collectors.toList());

案例五:list转Map,方法一样,但是map需要指定key、和value,转为年龄为key,Student对象为value的Map

List<Studengt> list = new ArrayList<>();
        list.add(new Studengt("aa",1));
        list.add(new Studengt("bb",2));
        list.add(new Studengt("cc",9));
        Map<Integer, Studengt> collect1 = list.stream().collect(Collectors.toMap(x -> x.getAge(), x -> x));

map中key唯一,当使用合并流,流转map时,如果指定的key一样,则会报错,但也有解决办法

List<Studengt> list = new ArrayList<>();
        list.add(new Studengt("aa",1));
        list.add(new Studengt("bb",2));
        list.add(new Studengt("cc",9));
        List<Studengt> list2 = new ArrayList<>();
        list2.add(new Studengt("aa",1));
        list2.add(new Studengt("bbc",8));
        list2.add(new Studengt("ddd",9));
        Stream.concat(list.stream(),list2.stream()).collect(Collectors.toMap(x->x.getAge(),x->x));

两个map的key冲突时,解决办法:指定冲突时,使用哪一个value。

List<Studengt> list = new ArrayList<>();
        list.add(new Studengt("aa",1));
        list.add(new Studengt("bb",2));
        list.add(new Studengt("cc",9));
        List<Studengt> list2 = new ArrayList<>();
        list2.add(new Studengt("aa",1));
        list2.add(new Studengt("bbc",8));
        list2.add(new Studengt("ddd",9));
Map<Integer, Studengt> collect = Stream.concat(list.stream(), list2.stream()).collect(Collectors.toMap((x) -> x.getAge(), x -> x, (x1, x2) -> x1));
        collect.forEach((key,va)-> System.out.println(key+"+++++"+va));
        //输出结果
1+++++Studengt{name='aa', age=1}
2+++++Studengt{name='bb', age=2}
8+++++Studengt{name='bbc', age=8}
9+++++Studengt{name='cc', age=9}

默认是HashMap类型,如何指定为TreeMap类型?
与List指定LinkList一样,传入构造方法的引用,此时是自然排序

Map<Integer, Studengt> collect = Stream.concat(list.stream(), list2.stream()).collect(Collectors.toMap((x) -> x.getAge(), x -> x, (x1, x2) -> x1, TreeMap::new));

如何指定为TreeMap类型,指定排序,不用自然排序?

Supplier<TreeMap<Integer,Studengt>> treeMapSupplier = ()->new TreeMap<>(Comparator.reverseOrder());
        Map<Integer, Studengt> collect = Stream.concat(list.stream(), list2.stream()).collect(Collectors.toMap((x) -> x.getAge(), x -> x, (x1, x2) -> x1, treeMapSupplier));
        collect.forEach((key,va)-> System.out.println(key+"+++++"+va));

List转Set

如果使用TreeSet,则元素需要实现Compare接口
实体类改造:

public class Studengt implements Comparable<Studengt>{
@Override
    public int compareTo(Studengt o) {
        return this.getAge()-o.getAge();
    }

示例六List转Set

System.out.println("===========hashSet=============");
        //转set,默认hashset
        Set<Studengt> collect = concat.collect(Collectors.toSet());
        collect.forEach(System.out::println);
        System.out.println("===========TreeSet,默认排序=============");
        //转TreeSet
        TreeSet<Studengt> collect2 = Stream.concat(list.stream(), list2.stream()).collect(Collectors.toCollection(TreeSet::new));
        collect2.forEach(System.out::println);
        System.out.println("===========TreeSet,age降序排序=============");
        //转TreeSet,并且指定age降序排序
        Supplier<TreeSet<Studengt>> treeSetSupplier = ()->new TreeSet<>((o1,o2)->(int)(o2.getAge()-o1.getAge()));
        TreeSet<Studengt> collect3 = Stream.concat(list.stream(), list2.stream()).collect(Collectors.toCollection(treeSetSupplier));
        collect3.forEach(System.out::println);
    }