lambda表达式:Comparator,自定义收集器Collector

Comparator

常用方法

  	/**
     返回一个比较器,该比较器强制此比较器的相反顺序(取反)
     */
    default Comparator<T> reversed() {
        return Collections.reverseOrder(this);
    }
    /*
	接受从类型T中提取可比较排序键的函数,并返回按该排序键进行比较的	
	(通过传过来的行为进行排序)thenComparingInt这种类似 只是统一类型避免类型转换
	*/
     public static <T, U extends Comparable<? super U>> Comparator<T> comparing(
            Function<? super T, ? extends U> keyExtractor)
    {
        Objects.requireNonNull(keyExtractor);
        return (Comparator<T> & Serializable)
            (c1, c2) -> keyExtractor.apply(c1).compareTo(keyExtractor.apply(c2));
    }
	/*
	接受从类型T中提取排序键的函数,并返回比较器<T>,该比较器使用(参数中)指定的比较器与排序键进行比较。
	将keyExtractor得到的数据通过keyComparator.apply()进行比较 返回比较器
	*/
   public static <T, U> Comparator<T> comparing(
            Function<? super T, ? extends U> keyExtractor,
            Comparator<? super U> keyComparator)
    {
        Objects.requireNonNull(keyExtractor);
        Objects.requireNonNull(keyComparator);
        return (Comparator<T> & Serializable)
            (c1, c2) -> keyComparator.compare(keyExtractor.apply(c1),
                                              keyExtractor.apply(c2));
    }
  /*
  返回一个字典顺序比较器和另一个比较器。如果比较器认为两个元素相等,
  即compare(a,b)==0(第一个比较器无法确定两个参数比较顺序),则使用other来确定顺序。
 	(当Comparator.comparingInt比较出现相同数据及 两者比较等于0时 
 		此时才会去调用thenComparing 当前比较器
      比较是正或者负 都不会去调用thenComparing)因为正负前比较器就可以确定大小调用后面没意义

注意:该方法不是先通过第一个比较器比完,在使用第二个比较器再次,第二个比较器是辅助第一个进行比较的
  */
  default Comparator<T> thenComparing(Comparator<? super T> other) {
        Objects.requireNonNull(other);
        return (Comparator<T> & Serializable) (c1, c2) -> {
            int res = compare(c1, c2);
            /*
            res 不等于0 说明已经区分大小写 比较顺序以第一个为主 第二个为辅
            第一个比较器无法判断时在使用第二个比较器进行比较 
            */
            return (res != 0) ? res : other.compare(c1, c2);
        };
    }

示例

public class MyComparatorTest {

    public static void main(String[] args) {

        List<String> list = Arrays.asList("hello", "world", "nihao", "welcome");
//        list.stream().sorted((x,y)->y.length()-x.length()).forEach(System.out::println);

        /*
            reversed 强行将一个比较器取反
         */
//        Collections.sort(list, Comparator.comparingInt(String::length).reversed());
//        list.forEach(System.out::println);
        System.out.println("===========");
        /*
        Comparator.comparingInt 是将比较器再拿来比较此时推断不出来 参数类型
        comparingInt(ToIntFunction<? super T> keyExtractor) 输入的参数?super T T也有可能是父类型之上的无法推断 所以此时无法推断时就会直接设为基类类型
        所以显式添加参数类型只能是T类型(元素类型)之上的类型
         */
//        Collections.sort(list, Comparator.comparingInt((String x) -> x.length()).reversed());
//        list.forEach(System.out::println);

        System.out.println("============");
//        list.sort(Comparator.comparingInt(String::length).reversed());
//        list.sort(Comparator.comparingInt((String x) -> x.length()).reversed());

        System.out.println("============");
        /*
            先通过length 升序比较,在使用thenComparing()首字母不区分大小写排序CASE_INSENSITIVE_ORDER
            当Comparator.comparingInt比较出现相同数据及 两者比较等于0时 此时才会去调用thenComparing 当前比较器
            比较是正或者负 都不会去调用thenComparing
         */
//        Collections.sort(list, Comparator.comparingInt(String::length).thenComparing(String.CASE_INSENSITIVE_ORDER));

//        Collections.sort(list, Comparator.comparingInt(String::length)
//                .thenComparing((x, y) -> x.toLowerCase().compareTo(y.toLowerCase())));

//        Collections.sort(list, Comparator.comparingInt(String::length).thenComparing(Comparator.comparing(String::toLowerCase)));
        System.out.println("=================");
        /*
        world
        nihao
        hello
        welcome
        前三个顺序逆转了  (具体看thenComparing )
         */
//        Collections.sort(list, Comparator.comparingInt(String::length)
//                .thenComparing(Comparator.comparing(String::toLowerCase, Comparator.reverseOrder())));

        System.out.println("=======");
//        Collections.sort(list, Comparator.comparingInt(String::length).reversed()
//                .thenComparing(Comparator.comparing(String::toLowerCase, Comparator.reverseOrder())));
//        list.forEach(System.out::println);

        System.out.println("============");
        /*
            如果比较等于0 则顺序不变 第一个thenComparing 进行字母排序 compare 比较!=0
            第二个thenComparing 也就未执行
         */
        Collections.sort(list, Comparator.comparingInt(String::length).reversed().
                thenComparing(Comparator.comparing(String::toLowerCase, Comparator.reverseOrder())).
                thenComparing(Comparator.reverseOrder()));
        list.forEach(System.out::println);
    }
}

自定义收集器

示例1

public class MySetCollector<T> implements Collector<T, Set<T>, Set<T>> {
    @Override
    public Supplier<Set<T>> supplier() {
        System.out.println("suppliper invoked");
        //返回的结果对象就是 A 中间结果容器
        return HashSet::new;
    }

    @Override
    public BiConsumer<Set<T>, T> accumulator() {
        System.out.println("accumulator invoked");
        return Set::add;
        //supplier 返回的中间容器可能跟此不对应
//        return HashSet::add;
    }

    @Override
    public BinaryOperator<Set<T>> combiner() {
        System.out.println("combiner invoked");
        return (x, y) -> {
            x.addAll(y);
            return x;
        };
    }

    @Override
    public Function<Set<T>, Set<T>> finisher() {
        System.out.println("finisher invoked");
//        return (x -> x);
        //等同上
        return Function.identity();
    }

    @Override
    public Set<Characteristics> characteristics() {
        System.out.println("characteristics invoked");
        /*
            unmodifiableSet: 方法返回只读不可变集合
            EnumSet 用于枚举类型的专用集合实现
            of 创建最初包含指定元素的枚举集。
            IDENTITY_FINISH 表示 一致性identity和结合性finisher是一个函数类型
            (也就是中间容器类型的和最终返回结果类型一致)
            UNORDERED 指示集合操作不承诺保留输入元素的相遇顺序。
            (如果结果容器没有内在顺序(如集合),则可能是这样。
         */
        return Collections.unmodifiableSet(EnumSet.of(IDENTITY_FINISH, UNORDERED));
    }

    public static void main(String[] args) {
        List<String> list = Arrays.asList("hello", "world", "welcome", "hello");
        //如果中间结果类型A和返回结果类型R一致finisher()方法将不会被调用
        Set<String> set = list.stream().collect(new MySetCollector<>());
        System.out.println(set);


    }
}

打印结果

suppliper invoked
accumulator invoked
combiner invoked
characteristics invoked
characteristics invoked
[world, hello, welcome]

在这里插入图片描述
collect实现类
如下代码如果设置IDENTITY_FINISH这个类型 此时中间容器类型和返回结果类型必须一致
在这里插入图片描述
调用收集器的方法
在这里插入图片描述

示例2(重要)

在这里插入图片描述

public class MyMapCollector<T> implements Collector<T, Set<T>, Map<T, T>> {

    @Override
    public Supplier<Set<T>> supplier() {
        System.out.println("suppliper invoked");
//        return HashSet::new;
        return () -> {
            /*
				多线程调用打印对应次--------- 说明每个线程都生成一个中间结果容器

                单线程调用 打印一次------ 说明值生成一个中间容器
             */
            System.out.println("-----------------");
            return new HashSet<T>();
        };
    }

    @Override
    public BiConsumer<Set<T>, T> accumulator() {
        System.out.println("accumulator invoked");
        return (x, y) -> {
            //如果多线程存在多个操作 可能会抛异常
//            System.out.println("accumulator: " + x + "," + Thread.currentThread().getName());

  /*
            如果是串行流或者并行流设置IDENTITY_FINISH特性时 直接把中间结果容器当做最终结果返回
            不会调用combiner()和finisher() 方法
             */
            x.add(y);
        };
    }

    @Override
    public BinaryOperator<Set<T>> combiner() {
        System.out.println("combiner invoked");//打印只是调用方法
        return (x, y) -> {
            /*
            如果并行流去操作收集器此时未设置Characteristics.CONCURRENT特性时
            此时会被调用(需收集多线程结果)返回行为(返回行为才算被调用)
    如果设置了改特性 依然不会被调用 (此时多线程操作一个中间容器,结果都在一个中间容器中)
            注意返回行为才算调用成功
             */
            System.out.println("x:" + x);
            x.addAll(y);
            return x;
        };
    }

    @Override
    public Function<Set<T>, Map<T, T>> finisher() {
        System.out.println("finisher invoked");
        return (x) -> {
            Map<T, T> collect = x.stream().collect(Collectors.toMap(Function.identity(), Function.identity()));
//            Map<T, T> map = new HashMap<>();
//            x.stream().forEach(y -> map.put(y, y));
            return collect;
        };
    }

    @Override
    public Set<Characteristics> characteristics() {
        System.out.println("characteristics invoked");
        /*
            UNORDERED 无序
            CONCURRENT 并行
         */
        return Collections.unmodifiableSet(EnumSet.of(Characteristics.UNORDERED, Characteristics.CONCURRENT));
    }

    public static void main(String[] args) {
        List<String> list = Arrays.asList("hello", "world", "hello", "a", "bv", "cc");
        for (int i = 0; i <= 100; ++i) {

            Set<String> set = new HashSet();
            set.addAll(list);
            System.out.println("set:" + set);
            System.out.println("============");
            /*
                当使用并行流操作收集器时收集器中方法characteristics
                未标识Characteristics.CONCURRENT类型(并行多个线程操作一个容器)
                此时每个线程对应一个中间容器此时 finisher就需要将他们进行合并
             */
            Map<String, String> map = set.parallelStream().collect(new MyMapCollector<>());
                     /**
             * parallel() 并行
             * sequential() 串行
             */
//            Map<String, String> map2 = set.stream().sequential().collect(new 		MyMapCollector<>()); //同上
            System.out.println(map);


        }

    }
}

当执行改收集器可能会抛出如下异常

在这里插入图片描述
ConcurrentModificationException:通常不允许一个线程在另一个线程迭代集合时修改该集合。一般来说,在这种情况下迭代的结果是不确定的

在这里插入图片描述
当我们加了改类型此时多个线程操作一个accumulator() 中间容器,可能存在一个线程在添加,另外一个线程在迭代查询

如何避免

当自定义收集器时
在这里插入图片描述

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值