Java Streams - Get a “symmetric difference list” from two other lists

前端 未结 6 2077
囚心锁ツ
囚心锁ツ 2021-01-05 01:17

Im trying to use Java 8 streams to combine lists. How can I get a \"symmetric difference list\" (all object that only exist in one list) from two existing lists. I know how

6条回答
  •  心在旅途
    2021-01-05 01:56

    A little bit math

    disjoint = A and B are disjoint if their intersect is empty.

    A disjoint is not a set, it is an indicator showing if two sets are disjoint or not. From your description I think you where searching the symmetric difference.

    Symmetric Difference

    But anyhow, if you only want to collect to new Lists then all you need is a collector.

    I made a method that creates an Collector. This Collector only "collects" values, where the predicate is evaluated to true. So if you are searching for the symmetric difference, than you only need a predicate.

      public void testDisjointLists() {
        List bigCarList = get5DefaultCars();
        List smallCarList = get3DefaultCars();
    
        Collector, ArrayList> inter
            = produceCollector(car -> {
              return bigCarList.contains(car) && smallCarList.contains(car);
            });
    
        Collector, ArrayList> symDiff
            = produceCollector(car -> {
              return bigCarList.contains(car) ^ smallCarList.contains(car);
            });
    
        //Get all cars in both list as one list
        List union
            = Stream.concat(bigCarList.stream(), smallCarList.stream()).distinct().collect(Collectors.toList());
    
        List intersect = union.stream().collect(inter);
    
        //Get all cars that only exist not exists in both Lists
        List symmetricDifference = union.stream().collect(symDiff);
    
        System.out.println("Union Cars:");
        union.stream().forEach(car -> System.out.println("Car: " + car));
        System.out.println("");
    
        System.out.println("Intersect Cars: ");
        intersect.stream().forEach(car -> System.out.println("Car: " + car));
        System.out.println("");
    
        System.out.println("Symmetric Difference: ");
        symmetricDifference.stream().forEach(car -> System.out.println("Car: " + car));
        System.out.println("");
      }
    
      public Collector, ArrayList> produceCollector(Predicate predicate) {
        Collector, ArrayList> collector = Collector.of(
            ArrayList::new,
            (al, car) -> {
              if (predicate.test(car)) {
                al.add(car);
              }
            },
            (al1, al2) -> {
              al1.addAll(al2);
              return al1;
            }
        );
        return collector;
      }
    

    For performance freaks

    After doing some research, it seems that the collector is about 14 times faster than a first filter solution.

    long before2 = System.nanoTime();
    List intersect2 = union.stream().filter(car -> {
      return bigCarList.contains(car) && smallCarList.contains(car);
    }).collect(Collectors.toList());
    long after2 = System.nanoTime();
    System.out.println("Time for first filter solution: " + (after2 - before2));
    
    
    long before = System.nanoTime();
    List intersect = union.stream().collect(inter);
    long after = System.nanoTime();
    System.out.println("Time for collector solution: " + (after - before));
    

    Time for first filter solution: 540906

    Time for collector solution: 37543

提交回复
热议问题