StreamUtils.java
package no.nav.data.common.utils;
import lombok.SneakyThrows;
import no.nav.data.common.exceptions.NotFoundException;
import org.springframework.core.io.ClassPathResource;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
import static java.util.Comparator.comparing;
import static java.util.function.Function.identity;
import static java.util.stream.Collectors.toList;
public final class StreamUtils {
private StreamUtils() {
}
/**
* Get change in two collections based on predicate to match equality
*
* @param before collection before
* @param after collection after
* @param comparator function to compare if elements are equal
* @param <T> the type of the collections
*/
public static <T> CollectionDifference<T> difference(Collection<T> before, Collection<T> after, Comparator<? super T> comparator) {
List<T> removed = new ArrayList<>(before);
List<T> shared = new ArrayList<>();
List<T> added = new ArrayList<>(after);
removed.removeIf(beforeElement -> {
for (T afterElement : after) {
if (comparator.compare(beforeElement, afterElement) == 0) {
shared.add(afterElement);
added.remove(afterElement);
return true;
}
}
return false;
});
return new CollectionDifference<>(new ArrayList<>(before), new ArrayList<>(after), removed, shared, added);
}
public static <T extends Comparable<T>> CollectionDifference<T> difference(Collection<T> before, Collection<T> after) {
return difference(before, after, comparing(identity()));
}
public static <T> Stream<T> safeStream(Iterable<T> iterable) {
return iterable == null ? Stream.empty() : StreamSupport.stream(iterable.spliterator(), false);
}
public static <T> List<T> nullToEmptyList(Collection<T> list) {
return list == null ? Collections.emptyList() : new ArrayList<>(list);
}
public static <T> List<T> copyOf(List<T> list) {
return list == null ? Collections.emptyList() : List.copyOf(list);
}
@SafeVarargs
public static <T> List<T> union(List<? extends T>... lists) {
ArrayList<T> list = new ArrayList<>();
for (List<? extends T> l : lists) {
list.addAll(l);
}
return list;
}
public static <K, V> Map<K, V> toMap(Iterable<V> from, Function<? super V, K> keyExtractor) {
return safeStream(from).collect(Collectors.toMap(keyExtractor, Function.identity()));
}
public static <T> List<T> distinctByKey(Iterable<T> from, Function<? super T, ?> keyExtractor) {
return filter(from, distinctByKey(keyExtractor));
}
public static <T> Predicate<T> distinctByKey(Function<? super T, ?> keyExtractor) {
Set<Object> seen = ConcurrentHashMap.newKeySet();
return t -> seen.add(keyExtractor.apply(t));
}
public static <T, F> List<T> convert(Collection<F> from, Function<F, T> converter) {
return safeStream(from).map(converter).filter(Objects::nonNull).collect(toList());
}
public static <T, F> List<T> convertFlat(Collection<F> from, Function<F, Collection<T>> converter) {
return safeStream(from).map(converter).filter(Objects::nonNull).flatMap(Collection::stream).collect(toList());
}
@SafeVarargs
public static <T, F> List<T> applyAll(Collection<F> from, Function<F, Collection<T>>... converters) {
return Stream.of(converters)
.map(f -> convert(from, f))
.flatMap(Collection::stream)
.flatMap(Collection::stream)
.collect(toList());
}
@SafeVarargs
public static <T, F> List<T> applyAll(F from, Function<F, Collection<T>>... converters) {
return Stream.of(converters)
.map(f -> f.apply(from))
.flatMap(Collection::stream)
.collect(toList());
}
public static <T> List<T> filter(Iterable<T> objects, Predicate<T> filter) {
return safeStream(objects).filter(filter).collect(toList());
}
public static <T, U extends Comparable<? super U>> List<T> filterCommonElements(Iterable<T> objects, Iterable<T> compareToObjects,
Function<? super T, ? extends U> keyExtractor) {
var comparator = Comparator.comparing(keyExtractor);
return filter(objects, object -> safeStream(compareToObjects).noneMatch(other -> comparator.compare(object, other) == 0));
}
public static <T> T find(Iterable<T> objects, Predicate<T> filter) {
return safeStream(objects).filter(filter).findFirst().orElseThrow(() -> new NotFoundException("could not find item"));
}
public static <T> Optional<T> tryFind(Iterable<T> objects, Predicate<T> filter) {
return safeStream(objects).filter(filter).findFirst();
}
@SafeVarargs
public static <T> T first(T... objects) {
return Stream.of(objects).filter(Objects::nonNull).findFirst().orElseThrow();
}
public static <T> T firstOrNull(Collection<T> objects) {
return safeStream(objects).findFirst().orElse(null);
}
@SneakyThrows
public static String readCpFile(String path) {
return org.springframework.util.StreamUtils.copyToString(new ClassPathResource(path).getInputStream(), StandardCharsets.UTF_8);
}
public static <T> boolean duplicates(Collection<T> items, Function<? super T, ?> keyExctactor) {
var distinct = distinctByKey(items, keyExctactor);
return distinct.size() != items.size();
}
}