All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.github.pellse.util.collection.CollectionUtils Maven / Gradle / Ivy

Go to download

Small library allowing to efficiently assemble entities from querying/merging external datasources or aggregating microservices

The newest version!
/*
 * Copyright 2024 Sebastien Pelletier
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package io.github.pellse.util.collection;

import java.util.*;
import java.util.Map.Entry;
import java.util.function.*;
import java.util.stream.Stream;

import static io.github.pellse.util.ObjectUtils.also;
import static io.github.pellse.util.ObjectUtils.ifNotNull;
import static java.util.Collections.emptySet;
import static java.util.LinkedHashMap.newLinkedHashMap;
import static java.util.Map.entry;
import static java.util.function.Function.identity;
import static java.util.stream.Collectors.*;
import static java.util.stream.StreamSupport.stream;

public interface CollectionUtils {

    static > Stream toStream(C iterable) {
        return iterable != null ? stream(iterable.spliterator(), false) : Stream.empty();
    }

    static > Stream concat(C collection1, C collection2) {
        return Stream.concat(toStream(collection1), toStream(collection2));
    }

    static boolean isEmpty(Iterable iterable) {

        return iterable == null ||
                (iterable instanceof Collection coll && coll.isEmpty()) ||
                !iterable.iterator().hasNext();
    }

    static boolean isNotEmpty(Iterable iterable) {
        return !isEmpty(iterable);
    }

    static boolean isEmpty(Map map) {
        return map == null || map.isEmpty();
    }

    static boolean isNotEmpty(Map map) {
        return !isEmpty(map);
    }

    static  Map nullToEmptyMap(Map value) {
        return nullToEmptyMap(value, Map::of);
    }

    static > T nullToEmptyMap(T value, Supplier defaultFactory) {
        return value != null ? value : defaultFactory.get();
    }

    static  List transform(Iterable from, Function mappingFunction) {
        return toStream(from).map(mappingFunction).toList();
    }

    static  Collection asCollection(Iterable iterable) {
        return iterable instanceof Collection coll ? coll : stream(iterable.spliterator(), false).toList();
    }

    static > C translate(Iterable from, Supplier collectionFactory) {
        return asCollection(from).stream().collect(toCollection(collectionFactory));
    }

    static int size(Iterable iterable) {
        return iterable == null ? 0 : asCollection(iterable).size();
    }

    static  Map transformMapKeys(Map map, Function keyMapper) {
        return transformMapKeys(map, (key, __) -> keyMapper.apply(key));
    }

    static  Map transformMapKeys(Map map, BiFunction keyMapper) {
        return transformMap(map, keyMapper, (key, value) -> value);
    }

    static  Map transformMapValues(Map map, Function valueMapper) {
        return transformMapValues(map, (__, value) -> valueMapper.apply(value));
    }

    static  Map transformMapValues(Map map, BiFunction valueMapper) {
        return transformMap(map, (key, value) -> key, valueMapper);
    }

    static  Map transformMap(Map map, BiFunction keyMapper, BiFunction valueMapper) {
        return toLinkedHashMap(map.entrySet(), e -> keyMapper.apply(e.getKey(), e.getValue()), e -> valueMapper.apply(e.getKey(), e.getValue()));
    }

    @SafeVarargs
    static  Map newMap(Consumer>... initializers) {
        return newMap(null, initializers);
    }

    @SafeVarargs
    static  Map newMap(Map map, Consumer>... initializers) {
        return newMap(map, LinkedHashMap::new, initializers);
    }

    @SafeVarargs
    static > M newMap(Map map, Supplier mapSupplier, Consumer>... initializers) {

        final var copyMap = mapSupplier.get();
        if (map != null) {
            copyMap.putAll(map);
        }

        for (var initializer : initializers) {
            initializer.accept(copyMap);
        }
        return copyMap;
    }

    static  Set diff(Iterable iterable1, Iterable iterable2) {

        final var coll1 = asCollection(iterable1);
        if (coll1.isEmpty()) {
            return emptySet();
        }

        final var finalSet = new HashSet(coll1);

        final var coll2 = asCollection(iterable2);
        if (coll2.isEmpty()) {
            return finalSet;
        }

        return also(finalSet, set -> set.removeAll(coll2));
    }

    static  Map diff(Map map1, Map map2) {
        return readAll(diff(map1.keySet(), map2.keySet()), map1);
    }

    static  Map readAll(Iterable keys, Map sourceMap) {
        return readAll(keys, sourceMap, identity());
    }

    static  Map readAll(Iterable keys, Map sourceMap, Function mappingFunction) {
        return readAll(keys, sourceMap, LinkedHashMap::new, mappingFunction);
    }

    static > Map readAll(Iterable keys, Map sourceMap, Supplier mapSupplier, Function mappingFunction) {
        return newMap(null, mapSupplier, map -> keys.forEach(key -> ifNotNull(sourceMap.get(key), value -> map.put(key, mappingFunction.apply(value)))));
    }

    static > VC removeDuplicates(
            Collection coll,
            Function keyExtractor,
            Function, VC> collectionConverter) {

        return removeDuplicates(toStream(coll), keyExtractor, collectionConverter);
    }

    private static > VC removeDuplicates(
            Stream stream,
            Function keyExtractor,
            Function, VC> collectionConverter) {

        return collectionConverter.apply(stream
                .collect(toMap(keyExtractor, identity(), (v1, v2) -> v2, LinkedHashMap::new))
                .values());
    }

    static  LinkedHashMap toLinkedHashMap(Map map) {
        return map instanceof LinkedHashMap lhm ? lhm : new LinkedHashMap<>(map);
    }

    static  LinkedHashMap toLinkedHashMap(Iterable iterable, Function keyExtractor) {
        return toLinkedHashMap(iterable, keyExtractor, identity());
    }

    static  LinkedHashMap toLinkedHashMap(Iterable iterable, Function keyExtractor, Function valueExtractor) {
        return toJavaMap(iterable, keyExtractor, valueExtractor, LinkedHashMap::newLinkedHashMap);
    }

    static  HashMap toHashMap(Iterable iterable, Function keyExtractor, Function valueExtractor) {
        return toJavaMap(iterable, keyExtractor, valueExtractor, HashMap::newHashMap);
    }

    static > M toJavaMap(Iterable iterable, Function keyExtractor, Function valueExtractor, IntFunction mapFactory) {
        final int size = size(iterable);
        return toStream(iterable).collect(toMap(keyExtractor, valueExtractor, (v1, v2) -> v2, () -> mapFactory.apply(size)));
    }

    static  Map> mergeMaps(
            Map> existingMap,
            Map> newMap,
            Function idResolver) {

        return mergeMaps(existingMap, newMap, idResolver, ArrayList::new);
    }

    static , ID> Map mergeMaps(
            Map existingMap,
            Map newMap,
            Function idResolver,
            Function, VC> collectionConverter) {

        final int size = existingMap.size() + newMap.size();

        return concat(existingMap.entrySet(), newMap.entrySet())
                .map(entry -> entry(entry.getKey(), removeDuplicates(entry.getValue(), idResolver, collectionConverter)))
                .collect(toMap(
                        Entry::getKey,
                        Entry::getValue,
                        (coll1, coll2) -> removeDuplicates(concat(coll1, coll2), idResolver, collectionConverter),
                        () -> newLinkedHashMap(size)));
    }

    @SafeVarargs
    static  Map mergeMaps(Map... maps) {
        return Stream.of(maps)
                .flatMap(map -> map.entrySet().stream())
                .collect(toMap(Entry::getKey, Entry::getValue, (v1, v2) -> v2, LinkedHashMap::new));
    }

    static  Map> subtractFromMap(
            Map> mapToSubtract,
            Map> srcMap,
            Function idResolver) {

        return subtractFromMap(mapToSubtract, srcMap, idResolver, ArrayList::new);
    }

    static , ID> Map subtractFromMap(
            Map mapToSubtract,
            Map srcMap,
            Function idResolver,
            Supplier collectionFactory) {

        return srcMap.entrySet().stream()
                .map(entry -> {
                    final var itemsToSubtract = mapToSubtract.get(entry.getKey());
                    if (itemsToSubtract == null)
                        return entry;

                    final var idsToSubtract = itemsToSubtract.stream()
                            .map(idResolver)
                            .collect(toSet());

                    final var newColl = toStream(entry.getValue())
                            .filter(element -> !idsToSubtract.contains(idResolver.apply(element)))
                            .collect(toCollection(collectionFactory));

                    return isNotEmpty(newColl) ? entry(entry.getKey(), newColl) : null;
                })
                .filter(Objects::nonNull)
                .collect(toMap(Entry::getKey, Entry::getValue, (v1, v2) -> v1, () -> newLinkedHashMap(srcMap.size())));
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy