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

org.neo4j.internal.helpers.collection.Iterables Maven / Gradle / Ivy

There is a newer version: 5.25.1
Show newest version
/*
 * Copyright (c) "Neo4j"
 * Neo4j Sweden AB [https://neo4j.com]
 *
 * This file is part of Neo4j.
 *
 * Neo4j is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see .
 */
package org.neo4j.internal.helpers.collection;

import static java.util.Objects.requireNonNull;

import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.Spliterator;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Stream;
import org.neo4j.function.Predicates;
import org.neo4j.function.ThrowingConsumer;
import org.neo4j.graphdb.Resource;
import org.neo4j.graphdb.ResourceIterable;
import org.neo4j.graphdb.ResourceIterator;
import org.neo4j.internal.helpers.Exceptions;

/**
 * Utility methods for processing iterables. Where possible, If the iterable implements
 * {@link Resource}, it will be {@link Resource#close() closed} when the processing
 * has been completed.
 */
public final class Iterables {
    private Iterables() {
        throw new AssertionError("no instance");
    }

    public static  Iterable empty() {
        return Collections.emptyList();
    }

    @SuppressWarnings("unchecked")
    public static  ResourceIterable emptyResourceIterable() {
        return (ResourceIterable) EmptyResourceIterable.EMPTY_RESOURCE_ITERABLE;
    }

    /**
     * Collect all the elements available in {@code iterable} and add them to the
     * provided {@code collection}.
     * 

* If the {@code iterable} implements {@link Resource} it will be * {@link Resource#close() closed} in a {@code finally} block after all * the items have been added. * * @param collection the collection to add items to. * @param iterable the iterable from which items will be collected * @param the type of elements in {@code iterable}. * @param the type of the collection to add the items to. * @return the {@code collection} that has been updated. */ public static > C addAll(C collection, Iterable iterable) { try { Iterator iterator = iterable.iterator(); try { while (iterator.hasNext()) { collection.add(iterator.next()); } } finally { Iterators.tryCloseResource(iterator); } } finally { tryCloseResource(iterable); } return collection; } public static Iterable filter(Predicate specification, Iterable i) { return new FilterIterable<>(i, specification); } public static List reverse(List iterable) { List list = asList(iterable); Collections.reverse(list); return list; } public static Iterable map(Function function, Iterable from) { return new MapIterable<>(from, function); } @SafeVarargs public static Iterable iterable(C... items) { return Arrays.asList(items); } @SuppressWarnings("unchecked") public static Iterable cast(Iterable iterable) { return (Iterable) iterable; } @SafeVarargs @SuppressWarnings("unchecked") public static Iterable concat(Iterable... iterables) { return concat(Arrays.asList((Iterable[]) iterables)); } public static Iterable concat(final Iterable> iterables) { return new CombiningIterable<>(iterables); } public static Iterable append(final C item, final Iterable iterable) { return () -> { final Iterator iterator = iterable.iterator(); return new Iterator<>() { T last = item; @Override public boolean hasNext() { return iterator.hasNext() || last != null; } @Override public T next() { if (iterator.hasNext()) { return iterator.next(); } try { return last; } finally { last = null; } } @Override public void remove() {} }; }; } public static Object[] asArray(Iterable iterable) { return asArray(Object.class, iterable); } @SuppressWarnings("unchecked") public static T[] asArray(Class componentType, Iterable iterable) { if (iterable == null) { return null; } List list = asList(iterable); return list.toArray((T[]) Array.newInstance(componentType, list.size())); } public static ResourceIterable asResourceIterable(final Iterable iterable) { if (iterable instanceof ResourceIterable) { return (ResourceIterable) iterable; } return new AbstractResourceIterable<>() { @Override protected ResourceIterator newIterator() { return Iterators.asResourceIterator(iterable.iterator()); } @Override protected void onClosed() { tryCloseResource(iterable); } }; } /** * Returns the given iterable's first element or {@code null} if no * element found. *

* If the {@code iterable} implements {@link Resource}, then it will be closed in a {@code finally} block * after the items have been joined. *

* If the {@link Iterable#iterator() iterator} created by the {@code iterable} implements {@link Resource} * it will be {@link Resource#close() closed} in a {@code finally} block after the items have been joined. * * @param values the {@link Iterable} to get elements from. * @param separator the separator to use between the items in {@code values}. * @return the joined string. */ public static String toString(Iterable values, String separator) { Iterator it = values.iterator(); try { StringBuilder sb = new StringBuilder(); while (it.hasNext()) { sb.append(it.next()); if (it.hasNext()) { sb.append(separator); } } return sb.toString(); } finally { Iterators.tryCloseResource(it); tryCloseResource(values); } } /** * Returns the given iterable's first element or {@code null} if no * element found. *

* If the {@code iterable} implements {@link Resource}, then it will be closed in a {@code finally} block * after the first item has been retrieved, or failed to be retrieved. *

* If the {@link Iterable#iterator() iterator} created by the {@code iterable} implements {@link Resource} * it will be {@link Resource#close() closed} in a {@code finally} block after the single item * has been retrieved, or failed to be retrieved. * * @param the type of elements in {@code iterable}. * @param iterable the {@link Iterable} to get elements from. * @return the first element in the {@code iterable}, or {@code null} if no * element found. */ public static T firstOrNull(Iterable iterable) { try { return Iterators.firstOrNull(iterable.iterator()); } finally { tryCloseResource(iterable); } } /** * Returns the given iterable's first element. If no element is found a * {@link NoSuchElementException} is thrown. *

* If the {@code iterable} implements {@link Resource}, then it will be closed in a {@code finally} block * after the first item has been retrieved, or failed to be retrieved. * * @param the type of elements in {@code iterable}. * @param iterable the {@link Iterable} to get elements from. * @return the first element in the {@code iterable}. * @throws NoSuchElementException if no element found. */ public static T first(Iterable iterable) { try { return Iterators.first(iterable.iterator()); } finally { tryCloseResource(iterable); } } /** * Returns the given iterable's last element. If no element is found a * {@link NoSuchElementException} is thrown. *

* If the {@code iterable} implements {@link Resource}, then it will be closed in a {@code finally} block * after the last item has been retrieved, or failed to be retrieved. * * @param the type of elements in {@code iterable}. * @param iterable the {@link Iterable} to get elements from. * @return the last element in the {@code iterable}. * @throws NoSuchElementException if no element found. */ public static T last(Iterable iterable) { try { return Iterators.last(iterable.iterator()); } finally { tryCloseResource(iterable); } } /** * Returns the given iterable's single element or {@code null} if no * element found. If there is more than one element in the iterable a * {@link NoSuchElementException} will be thrown. *

* If the {@code iterable} implements {@link Resource}, then it will be closed in a {@code finally} block * after the single item has been retrieved, or failed to be retrieved. *

* If the {@link Iterable#iterator() iterator} created by the {@code iterable} implements {@link Resource} * it will be {@link Resource#close() closed} in a {@code finally} block after the single item * has been retrieved, or failed to be retrieved. * * @param the type of elements in {@code iterable}. * @param iterable the {@link Iterable} to get elements from. * @return the single element in {@code iterable}, or {@code null} if no * element found. * @throws NoSuchElementException if more than one element was found. */ public static T singleOrNull(Iterable iterable) { try { return Iterators.singleOrNull(iterable.iterator()); } finally { tryCloseResource(iterable); } } /** * Returns the given iterable's single element. If there are no elements * or more than one element in the iterable a {@link NoSuchElementException} * will be thrown. *

* If the {@code iterable} implements {@link Resource}, then it will be closed in a {@code finally} block * after the single item has been retrieved, or failed to be retrieved. *

* If the {@link Iterable#iterator() iterator} created by the {@code iterable} implements {@link Resource} * it will be {@link Resource#close() closed} in a {@code finally} block after the single item * has been retrieved, or failed to be retrieved. * * @param the type of elements in {@code iterable}. * @param iterable the {@link Iterable} to get elements from. * @return the single element in the {@code iterable}. * @throws NoSuchElementException if there isn't exactly one element. */ public static T single(Iterable iterable) { try { return Iterators.single(iterable.iterator()); } finally { tryCloseResource(iterable); } } /** * Returns the given iterable's single element or {@code itemIfNone} if no * element found. If there is more than one element in the iterable a * {@link NoSuchElementException} will be thrown. *

* If the {@code iterable} implements {@link Resource}, then it will be closed in a {@code finally} block * after the single item has been retrieved, or failed to be retrieved. *

> * If the {@link Iterable#iterator() iterator} created by the {@code iterable} implements {@link Resource} * it will be {@link Resource#close() closed} in a {@code finally} block after the single item * has been retrieved, or failed to be retrieved. * * @param the type of elements in {@code iterable}. * @param iterable the {@link Iterable} to get elements from. * @param itemIfNone item to use if none is found * @return the single element in {@code iterable}, or {@code null} if no * element found. * @throws NoSuchElementException if more than one element was found. */ public static T single(Iterable iterable, T itemIfNone) { try { return Iterators.single(iterable.iterator(), itemIfNone); } finally { tryCloseResource(iterable); } } /** * Counts the number of items in the {@code iterable} by looping through it. *

* If the {@code iterable} implements {@link Resource}, then it will be closed in a {@code finally} block * after all its items have been counted. *

* If the {@link Iterable#iterator() iterator} created by the {@code iterable} implements {@link Resource} * it will be {@link Resource#close() closed} in a {@code finally} block after the items have been counted. * * @param the type of items in the iterator. * @param iterable the {@link Iterable} to count items in. * @return the number of items found in {@code iterable}. */ public static long count(Iterable iterable) { return count(iterable, Predicates.alwaysTrue()); } /** * Counts the number of filtered items in the {@code iterable} by looping through it. *

* If the {@code iterable} implements {@link Resource}, then it will be closed in a {@code finally} block * after all its items have been counted. *

* If the {@link Iterable#iterator() iterator} created by the {@code iterable} implements {@link Resource} * it will be {@link Resource#close() closed} in a {@code finally} block after the items have been counted. * * @param the type of items in the iterator. * @param iterable the {@link Iterable} to count items in. * @param filter the filter to test items against * @return the number of found in {@code iterable}. */ public static long count(Iterable iterable, Predicate filter) { try { return Iterators.count(iterable.iterator(), filter); } finally { tryCloseResource(iterable); } } /** * Creates a collection from an iterable. *

* If the {@code iterable} implements {@link Resource}, then it will be closed in a {@code finally} block * after all its items have been added. *

* If the {@link Iterable#iterator() iterator} created by the {@code iterable} implements {@link Resource} * it will be {@link Resource#close() closed} in a {@code finally} block after all the items have been added. * * @param iterable The iterable to create the collection from. * @param The generic type of both the iterable and the collection. * @return a collection containing all items from the iterable. */ public static Collection asCollection(Iterable iterable) { return addAll(new ArrayList<>(), iterable); } /** * Creates a list from an iterable. *

* If the {@code iterable} implements {@link Resource}, then it will be closed in a {@code finally} block * after all its items have been added. *

* If the {@link Iterable#iterator() iterator} created by the {@code iterable} implements {@link Resource} * it will be {@link Resource#close() closed} in a {@code finally} block after all the items have been added. * * @param iterable The iterable to create the list from. * @param The generic type of both the iterable and the list. * @return a list containing all items from the iterable. */ public static List asList(Iterable iterable) { return addAll(new ArrayList<>(), iterable); } /** * Creates a {@link Set} from an {@link Iterable}. *

* If the {@code iterable} implements {@link Resource}, then it will be closed in a {@code finally} block * after all its items have been added. *

* If the {@link Iterable#iterator() iterator} created by the {@code iterable} implements {@link Resource} * it will be {@link Resource#close() closed} in a {@code finally} block after all the items have been added. * * @param iterable The items to create the set from. * @param The generic type of items. * @return a set containing all items from the {@link Iterable}. */ public static Set asSet(Iterable iterable) { return addAll(new HashSet<>(), iterable); } /** * Creates a {@link Set} from an {@link Iterable}. *

* If the {@code iterable} implements {@link Resource}, then it will be closed in a {@code finally} block * after all its items have been added. * * @param iterable The items to create the set from. * @param The generic type of items. * @return a set containing all items from the {@link Iterable}. */ public static Set asUniqueSet(Iterable iterable) { try { return Iterators.addToCollectionUnique(iterable, new HashSet<>()); } finally { tryCloseResource(iterable); } } public static Iterable asIterable(final long... array) { return () -> Iterators.asIterator(array); } public static Iterable asIterable(final int... array) { return () -> Iterators.asIterator(array); } @SafeVarargs public static Iterable asIterable(final T... array) { return () -> Iterators.iterator(array); } public static ResourceIterable resourceIterable(final Iterable iterable) { return new AbstractResourceIterable<>() { @Override protected ResourceIterator newIterator() { Iterator iterator = iterable.iterator(); Resource resource = (iterator instanceof Resource) ? (Resource) iterator : Resource.EMPTY; return Iterators.resourceIterator(iterator, resource); } @Override protected void onClosed() { tryCloseResource(iterable); } }; } public static Iterable option(final T item) { if (item == null) { return Collections.emptyList(); } return () -> Iterators.iterator(item); } /** * Create a stream from the given iterable. *

* Note: returned stream needs to be closed via {@link Stream#close()} if the given iterable implements * {@link Resource}. * * @param iterable the iterable to convert to stream * @param the type of elements in the given iterable * @return stream over the iterable elements * @throws NullPointerException when the given iterable is {@code null} */ public static Stream stream(Iterable iterable) { return stream(iterable, 0); } /** * Create a stream from the given iterable with given characteristics. *

* Note: returned stream needs to be closed via {@link Stream#close()} if the given iterable implements * {@link Resource}. * * @param iterable the iterable to convert to stream * @param characteristics the logical OR of characteristics for the underlying {@link Spliterator} * @param the type of elements in the given iterable * @return stream over the iterable elements * @throws NullPointerException when the given iterable is {@code null} */ public static Stream stream(Iterable iterable, int characteristics) { requireNonNull(iterable); return Iterators.stream(iterable.iterator(), characteristics).onClose(() -> tryCloseResource(iterable)); } /** * Method for calling a lambda function on many objects. The first exception to be encountered will be * thrown and subsequent processing of the remaining items will be aborted. *

* If the {@code iterable} implements {@link Resource}, then it will be closed in a {@code finally} block * after all its items have been consumed. * * @param iterable iterable to iterate over * @param consumer lambda function to call on each object passed */ public static void forEach(Iterable iterable, Consumer consumer) { try { for (final var item : iterable) { consumer.accept(item); } } finally { tryCloseResource(iterable); } } /** * Method for calling a lambda function on many objects when it is expected that the function might * throw an exception. First exception will be thrown and subsequent will be suppressed. * This method guarantees that all subjects will be consumed, unless {@link Error} happens. * * @param the type of exception anticipated, inferred from the lambda * @param subjects {@link Iterable} of objects to call the function on * @param consumer lambda function to call on each object passed * @throws E if consumption fails with this exception */ @SuppressWarnings("unchecked") public static void safeForAll(Iterable subjects, ThrowingConsumer consumer) throws E { try { E exception = null; for (T instance : subjects) { try { consumer.accept(instance); } catch (Throwable t) { exception = Exceptions.chain(exception, (E) t); } } if (exception != null) { throw exception; } } finally { tryCloseResource(subjects); } } public static List union(List list1, List list2) { requireNonNull(list1); requireNonNull(list2); var result = new ArrayList(list1.size() + list2.size()); result.addAll(list1); result.addAll(list2); return result; } /** * Close the provided {@code iterable} if it implements {@link Resource}. * * @param iterable the iterable to check for closing */ public static void tryCloseResource(Iterable iterable) { if (iterable instanceof Resource closeable) { closeable.close(); } } private static class EmptyResourceIterable implements ResourceIterable { private static final ResourceIterable EMPTY_RESOURCE_ITERABLE = new EmptyResourceIterable<>(); @Override public ResourceIterator iterator() { return Iterators.emptyResourceIterator(); } @Override public void close() { // no-op } } }