package com.supaham.commons; import com.google.common.collect.Lists; import com.supaham.commons.utils.CollectionUtils; import java.util.ArrayList; import java.util.Collection; import java.util.Comparator; import java.util.List; import java.util.Optional; import java.util.stream.Collector; import java.util.stream.Stream; public class CommonCollectors { /** * Creates a {@link Collector} that represents its elements in reverse order, using an * {@link ArrayList}. * <p/> * Thanks to <a href="http://stackoverflow.com/questions/24010109/java-8-stream-reverse-order"> * http://stackoverflow.com/questions/24010109/java-8-stream-reverse-order</a> * * @param <T> type of list to return * * @return a list collector in reversed order */ public static <T> Collector<T, List<T>, List<T>> inReverse() { return Collector.of(ArrayList::new, List::add, (l, r) -> { l.addAll(r); return l; }, Lists::<T>reverse); } /** * Creates a {@link Collector} random element from a {@link Stream}. This method collects the stream and calls * {@link CollectionUtils#getRandomElement(Collection)} which supplies the final result. * * @param <T> type of object to return * * @return random element */ public static <T> Collector<T, List<T>, Optional<T>> singleRandom() { // TODO make this more efficient by not having to create a list and add. return Collector.of(ArrayList::new, List::add, (l, r) -> { l.addAll(r); return l; }, l -> l.isEmpty() ? Optional.empty() : Optional.ofNullable(CollectionUtils.getRandomElement(l))); } // http://stackoverflow.com/a/29339106/2355760 /** * Returns a collector for a {@link List} collection of the minimum results with the given comparator. * <p /> * If a list of integers such as [1, 3, 2] were given, the number 1 would be returned in a List. However, if there * were two ones, such as [1, 3, 2, 1], then the number 1 would be returned twice in a List. * * @param comp comparator to test for minimum result * @param <T> type of object to compare * * @return list collector */ public static <T> Collector<T, ?, List<T>> minList(Comparator<? super T> comp) { return Collector.of( ArrayList::new, (list, t) -> { int c; if (list.isEmpty() || (c = comp.compare(t, list.get(0))) == 0) { list.add(t); } else if (c < 0) { list.clear(); list.add(t); } }, (list1, list2) -> { if (list1.isEmpty()) { return list2; } if (list2.isEmpty()) { return list1; } int r = comp.compare(list1.get(0), list2.get(0)); if (r > 0) { return list2; } else if (r < 0) { return list1; } else { list1.addAll(list2); return list1; } }); } /** * Returns a collector for a {@link List} collection of the maximum results with the given comparator. * <p /> * If a list of integers such as [1, 3, 2] were given, the number 3 would be returned in a List. However, if there * were two threes, such as [3, 1, 3, 2], then the number 3 would be returned twice in a List. * * @param comp comparator to test for maximum result * @param <T> type of object to compare * * @return list collector */ public static <T> Collector<T, ?, List<T>> maxList(Comparator<? super T> comp) { return Collector.of( ArrayList::new, (list, t) -> { int c; if (list.isEmpty() || (c = comp.compare(t, list.get(0))) == 0) { list.add(t); } else if (c > 0) { list.clear(); list.add(t); } }, (list1, list2) -> { if (list1.isEmpty()) { return list2; } if (list2.isEmpty()) { return list1; } int r = comp.compare(list1.get(0), list2.get(0)); if (r < 0) { return list2; } else if (r > 0) { return list1; } else { list1.addAll(list2); return list1; } }); } private CommonCollectors() { throw new AssertionError("nop"); } }