StreamUtils.java

  1. package no.nav.data.common.utils;

  2. import lombok.SneakyThrows;
  3. import no.nav.data.common.exceptions.NotFoundException;
  4. import org.springframework.core.io.ClassPathResource;

  5. import java.nio.charset.StandardCharsets;
  6. import java.util.ArrayList;
  7. import java.util.Collection;
  8. import java.util.Collections;
  9. import java.util.Comparator;
  10. import java.util.List;
  11. import java.util.Map;
  12. import java.util.Objects;
  13. import java.util.Optional;
  14. import java.util.Set;
  15. import java.util.concurrent.ConcurrentHashMap;
  16. import java.util.function.Function;
  17. import java.util.function.Predicate;
  18. import java.util.stream.Collectors;
  19. import java.util.stream.Stream;
  20. import java.util.stream.StreamSupport;

  21. import static java.util.Comparator.comparing;
  22. import static java.util.function.Function.identity;
  23. import static java.util.stream.Collectors.toList;

  24. public final class StreamUtils {

  25.     private StreamUtils() {
  26.     }

  27.     /**
  28.      * Get change in two collections based on predicate to match equality
  29.      *
  30.      * @param before collection before
  31.      * @param after collection after
  32.      * @param comparator function to compare if elements are equal
  33.      * @param <T> the type of the collections
  34.      */
  35.     public static <T> CollectionDifference<T> difference(Collection<T> before, Collection<T> after, Comparator<? super T> comparator) {
  36.         List<T> removed = new ArrayList<>(before);
  37.         List<T> shared = new ArrayList<>();
  38.         List<T> added = new ArrayList<>(after);
  39.         removed.removeIf(beforeElement -> {
  40.             for (T afterElement : after) {
  41.                 if (comparator.compare(beforeElement, afterElement) == 0) {
  42.                     shared.add(afterElement);
  43.                     added.remove(afterElement);
  44.                     return true;
  45.                 }
  46.             }
  47.             return false;
  48.         });
  49.         return new CollectionDifference<>(new ArrayList<>(before), new ArrayList<>(after), removed, shared, added);
  50.     }

  51.     public static <T extends Comparable<T>> CollectionDifference<T> difference(Collection<T> before, Collection<T> after) {
  52.         return difference(before, after, comparing(identity()));
  53.     }

  54.     public static <T> Stream<T> safeStream(Iterable<T> iterable) {
  55.         return iterable == null ? Stream.empty() : StreamSupport.stream(iterable.spliterator(), false);
  56.     }

  57.     public static <T> List<T> nullToEmptyList(Collection<T> list) {
  58.         return list == null ? Collections.emptyList() : new ArrayList<>(list);
  59.     }

  60.     public static <T> List<T> copyOf(List<T> list) {
  61.         return list == null ? Collections.emptyList() : List.copyOf(list);
  62.     }

  63.     @SafeVarargs
  64.     public static <T> List<T> union(List<? extends T>... lists) {
  65.         ArrayList<T> list = new ArrayList<>();
  66.         for (List<? extends T> l : lists) {
  67.             list.addAll(l);
  68.         }
  69.         return list;
  70.     }

  71.     public static <K, V> Map<K, V> toMap(Iterable<V> from, Function<? super V, K> keyExtractor) {
  72.         return safeStream(from).collect(Collectors.toMap(keyExtractor, Function.identity()));
  73.     }

  74.     public static <T> List<T> distinctByKey(Iterable<T> from, Function<? super T, ?> keyExtractor) {
  75.         return filter(from, distinctByKey(keyExtractor));
  76.     }

  77.     public static <T> Predicate<T> distinctByKey(Function<? super T, ?> keyExtractor) {
  78.         Set<Object> seen = ConcurrentHashMap.newKeySet();
  79.         return t -> seen.add(keyExtractor.apply(t));
  80.     }

  81.     public static <T, F> List<T> convert(Collection<F> from, Function<F, T> converter) {
  82.         return safeStream(from).map(converter).filter(Objects::nonNull).collect(toList());
  83.     }

  84.     public static <T, F> List<T> convertFlat(Collection<F> from, Function<F, Collection<T>> converter) {
  85.         return safeStream(from).map(converter).filter(Objects::nonNull).flatMap(Collection::stream).collect(toList());
  86.     }

  87.     @SafeVarargs
  88.     public static <T, F> List<T> applyAll(Collection<F> from, Function<F, Collection<T>>... converters) {
  89.         return Stream.of(converters)
  90.                 .map(f -> convert(from, f))
  91.                 .flatMap(Collection::stream)
  92.                 .flatMap(Collection::stream)
  93.                 .collect(toList());
  94.     }

  95.     @SafeVarargs
  96.     public static <T, F> List<T> applyAll(F from, Function<F, Collection<T>>... converters) {
  97.         return Stream.of(converters)
  98.                 .map(f -> f.apply(from))
  99.                 .flatMap(Collection::stream)
  100.                 .collect(toList());
  101.     }

  102.     public static <T> List<T> filter(Iterable<T> objects, Predicate<T> filter) {
  103.         return safeStream(objects).filter(filter).collect(toList());
  104.     }

  105.     public static <T, U extends Comparable<? super U>> List<T> filterCommonElements(Iterable<T> objects, Iterable<T> compareToObjects,
  106.             Function<? super T, ? extends U> keyExtractor) {
  107.         var comparator = Comparator.comparing(keyExtractor);
  108.         return filter(objects, object -> safeStream(compareToObjects).noneMatch(other -> comparator.compare(object, other) == 0));
  109.     }

  110.     public static <T> T find(Iterable<T> objects, Predicate<T> filter) {
  111.         return safeStream(objects).filter(filter).findFirst().orElseThrow(() -> new NotFoundException("could not find item"));
  112.     }

  113.     public static <T> Optional<T> tryFind(Iterable<T> objects, Predicate<T> filter) {
  114.         return safeStream(objects).filter(filter).findFirst();
  115.     }

  116.     @SafeVarargs
  117.     public static <T> T first(T... objects) {
  118.         return Stream.of(objects).filter(Objects::nonNull).findFirst().orElseThrow();
  119.     }

  120.     public static <T> T firstOrNull(Collection<T> objects) {
  121.         return safeStream(objects).findFirst().orElse(null);
  122.     }

  123.     @SneakyThrows
  124.     public static String readCpFile(String path) {
  125.         return org.springframework.util.StreamUtils.copyToString(new ClassPathResource(path).getInputStream(), StandardCharsets.UTF_8);
  126.     }

  127.     public static <T> boolean duplicates(Collection<T> items, Function<? super T, ?> keyExctactor) {
  128.         var distinct = distinctByKey(items, keyExctactor);
  129.         return distinct.size() != items.size();
  130.     }
  131. }