/*
* This file is part of JGrasstools (http://www.jgrasstools.org)
* (C) HydroloGIS - www.hydrologis.com
*
* JGrasstools 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 <http://www.gnu.org/licenses/>.
*/
package org.jgrasstools.gears.utils;
import java.util.List;
import java.util.LongSummaryStatistics;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.TreeSet;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.function.ToLongFunction;
import java.util.stream.Collectors;
import java.util.stream.DoubleStream;
import java.util.stream.IntStream;
import java.util.stream.LongStream;
import java.util.stream.Stream;
/**
* Utilities for Streams. This more a streams j8 documentation then something to really use.
*
* <p>Use .peek(e->sysout) to debug without interference on the stream's items</p>
* <p>Use primitive streams were possible: {@link IntStream}, {@link LongStream}, {@link DoubleStream}.</p>
*
* <p>Intermediate Methods, which produce again streams:
* <ul>
* <li>map</li>
* <li>filter </li>
* <li>distinct </li>
* <li>sorted </li>
* <li>peek </li>
* <li>limit </li>
* <li>substream </li>
* <li>parallel </li>
* <li>sequential </li>
* <li>unordered</li>
* <ul>
* </p>
* <p>Terminate Methods, after which the stream is consumed and no more operations can be performed on it:
* <li>forEach</li>
* <li>forEachOrdered </li>
* <li>toArray </li>
* <li>reduce </li>
* <li>collect </li>
* <li>min </li>
* <li>max </li>
* <li>count </li>
* <li>anyMatch </li>
* <li>allMatch </li>
* <li>noneMatch </li>
* <li>findFirst </li>
* <li>findAny </li>
* <li>iterator</li>
* </p>
* <p>Short-circuit Methods, that stop stream processing once conditions are satisfied.
* <li>anyMatch</li>
* <li>allMatch </li>
* <li>noneMatch </li>
* <li>findFirst </li>
* <li>findAny </li>
* <li>limit </li>
* <li>substream</li>
* </p>
*
* @author Antonello Andrea (www.hydrologis.com)
*/
public class StreamUtils {
public static <T> Stream<T> fromArray( T[] array ) {
return Stream.of(array);
}
public static <T> Stream<T> fromList( List<T> list ) {
return list.stream();
}
public static <T> Stream<T> fromListParallel( List<T> list ) {
return list.parallelStream();
}
public static <T> Stream<T> fromSupplier( Supplier<T> supplier ) {
return Stream.generate(supplier);
}
public static <T> Stream<T> distinct( Stream<T> stream ) {
return stream.distinct();
}
public static long countStrings( Stream<String> stream, boolean distinct, boolean ignoreCase ) {
if (ignoreCase && distinct) {
return stream.map(String::toLowerCase).distinct().count();
} else if (ignoreCase) {
return stream.map(String::toLowerCase).count();
} else if (distinct) {
return stream.distinct().count();
} else {
return stream.count();
}
}
public static String getString( Stream<Object> stream, String separator, String prefix, String suffix ) {
if (prefix != null || suffix != null) {
if (prefix == null)
prefix = "";
if (suffix == null)
suffix = "";
return stream.map(Object::toString).collect(Collectors.joining(separator, prefix, suffix));
}
return stream.map(Object::toString).collect(Collectors.joining(separator));
}
public static <T> List<T> toList( Stream<T> stream ) {
return stream.collect(Collectors.toList());
}
public static <T> Set<T> toSet( Stream<T> stream ) {
return stream.collect(Collectors.toSet());
}
public static <T> TreeSet<T> toTreeSet( Stream<T> stream ) {
return stream.collect(Collectors.toCollection(TreeSet::new));
}
/**
* Collect stream to map.
*
* @param stream the stream to convert.
* @param keySupplier the supplier for the key, ex. Object::getId()
* @param valueSupplier the value supplier, ex. Object::getValue()
* @return the map.
*/
public static <T, K, V> Map<K, V> toMap( Stream<T> stream, Function<T, K> keySupplier, Function<T, V> valueSupplier ) {
return stream.collect(Collectors.toMap(keySupplier, valueSupplier));
}
public static <T, K> Map<K, T> toMapWithToStringValue( Stream<T> stream, Function<T, K> keySupplier ) {
return stream.collect(Collectors.toMap(keySupplier, Function.identity()));
}
/**
* Collect a map by grouping based on the object's field.
*
* @param stream the stream to collect.
* @param groupingFunction the function supplying the grouping field, ex. Object::getField()
* @return the map of lists.
*/
public static <T, K, R> Map<R, List<T>> toMapGroupBy( Stream<T> stream, Function<T, R> groupingFunction ) {
return stream.collect(Collectors.groupingBy(groupingFunction));
// to get a set: Collectors.groupingBy(groupingFunction, Collectors.toSet())
}
/**
* Split a stream into a map with true and false.
*
* @param stream the stream to collect.
* @param predicate the predicate to use to define true or false, ex. e->e.getValue().equals("test")
* @return the map of lists.
*/
public static <T> Map<Boolean, List<T>> toMapPartition( Stream<T> stream, Predicate<T> predicate ) {
return stream.collect(Collectors.partitioningBy(predicate));
}
public static <T, R> void printLongStats( Stream<T> stream, ToLongFunction<T> summarizingFunction ) {
LongSummaryStatistics summary = stream.collect(Collectors.summarizingLong(summarizingFunction));
System.out.println(summary.getCount());
System.out.println(summary.getSum());
System.out.println(summary.getMin());
System.out.println(summary.getMax());
System.out.println(summary.getAverage());
}
/**
* Find an element in the stream.
*
* @param stream the stream to check. This should be parallel.
* @param predicate the predicate to use.
* @return the element or null.
*/
public static <T> T findAny( Stream<T> stream, Predicate<T> predicate ) {
Optional<T> element = stream.filter(predicate).findAny();
return element.orElse(null);
}
public static <T> Stream<T> findAll( Stream<T> stream, Predicate<T> predicate ) {
Stream<T> filteredStream = stream.filter(predicate);
return filteredStream;
}
}