├── src ├── main │ └── java │ │ └── br │ │ └── com │ │ └── zbra │ │ └── androidlinq │ │ ├── delegate │ │ ├── Predicate.java │ │ ├── Selector.java │ │ ├── SelectorByte.java │ │ ├── SelectorLong.java │ │ ├── Aggregator.java │ │ ├── Comparator.java │ │ ├── SelectorDouble.java │ │ ├── SelectorFloat.java │ │ ├── SelectorShort.java │ │ ├── SelectorInteger.java │ │ ├── EqualityComparator.java │ │ └── SelectorBigDecimal.java │ │ ├── exception │ │ └── MultipleElementsFoundException.java │ │ ├── IterableStream.java │ │ ├── ReverseStream.java │ │ ├── ListStream.java │ │ ├── MapStream.java │ │ ├── ReverseListIterator.java │ │ ├── Grouping.java │ │ ├── SkipStream.java │ │ ├── SelectStream.java │ │ ├── ArrayStream.java │ │ ├── TakeStream.java │ │ ├── WhereStream.java │ │ ├── OrderedStream.java │ │ ├── Linq.java │ │ ├── SelectManyStream.java │ │ ├── GroupByStream.java │ │ ├── OrderByStream.java │ │ ├── AbstractStream.java │ │ └── Stream.java └── test │ └── groovy │ └── br │ └── com │ └── zbra │ └── androidlinq │ ├── LinqTest.groovy │ └── StreamTest.groovy ├── .gitignore ├── LICENSE ├── android-linq.iml ├── pom.xml └── README.md /src/main/java/br/com/zbra/androidlinq/delegate/Predicate.java: -------------------------------------------------------------------------------- 1 | package br.com.zbra.androidlinq.delegate; 2 | 3 | public interface Predicate { 4 | boolean apply(T value); 5 | } -------------------------------------------------------------------------------- /src/main/java/br/com/zbra/androidlinq/delegate/Selector.java: -------------------------------------------------------------------------------- 1 | package br.com.zbra.androidlinq.delegate; 2 | 3 | public interface Selector { 4 | R select(T value); 5 | } 6 | -------------------------------------------------------------------------------- /src/main/java/br/com/zbra/androidlinq/delegate/SelectorByte.java: -------------------------------------------------------------------------------- 1 | package br.com.zbra.androidlinq.delegate; 2 | 3 | public interface SelectorByte extends Selector { 4 | } 5 | -------------------------------------------------------------------------------- /src/main/java/br/com/zbra/androidlinq/delegate/SelectorLong.java: -------------------------------------------------------------------------------- 1 | package br.com.zbra.androidlinq.delegate; 2 | 3 | public interface SelectorLong extends Selector { 4 | } 5 | -------------------------------------------------------------------------------- /src/main/java/br/com/zbra/androidlinq/delegate/Aggregator.java: -------------------------------------------------------------------------------- 1 | package br.com.zbra.androidlinq.delegate; 2 | 3 | public interface Aggregator { 4 | R aggregate(R r, T t); 5 | } 6 | -------------------------------------------------------------------------------- /src/main/java/br/com/zbra/androidlinq/delegate/Comparator.java: -------------------------------------------------------------------------------- 1 | package br.com.zbra.androidlinq.delegate; 2 | 3 | public interface Comparator { 4 | int compare(T t1, T t2); 5 | } 6 | -------------------------------------------------------------------------------- /src/main/java/br/com/zbra/androidlinq/delegate/SelectorDouble.java: -------------------------------------------------------------------------------- 1 | package br.com.zbra.androidlinq.delegate; 2 | 3 | public interface SelectorDouble extends Selector { 4 | } 5 | -------------------------------------------------------------------------------- /src/main/java/br/com/zbra/androidlinq/delegate/SelectorFloat.java: -------------------------------------------------------------------------------- 1 | package br.com.zbra.androidlinq.delegate; 2 | 3 | public interface SelectorFloat extends Selector { 4 | } 5 | -------------------------------------------------------------------------------- /src/main/java/br/com/zbra/androidlinq/delegate/SelectorShort.java: -------------------------------------------------------------------------------- 1 | package br.com.zbra.androidlinq.delegate; 2 | 3 | public interface SelectorShort extends Selector { 4 | } 5 | -------------------------------------------------------------------------------- /src/main/java/br/com/zbra/androidlinq/delegate/SelectorInteger.java: -------------------------------------------------------------------------------- 1 | package br.com.zbra.androidlinq.delegate; 2 | 3 | public interface SelectorInteger extends Selector{ 4 | } 5 | -------------------------------------------------------------------------------- /src/main/java/br/com/zbra/androidlinq/delegate/EqualityComparator.java: -------------------------------------------------------------------------------- 1 | package br.com.zbra.androidlinq.delegate; 2 | 3 | public interface EqualityComparator { 4 | boolean compare(T value1, T value2); 5 | } 6 | -------------------------------------------------------------------------------- /src/main/java/br/com/zbra/androidlinq/exception/MultipleElementsFoundException.java: -------------------------------------------------------------------------------- 1 | package br.com.zbra.androidlinq.exception; 2 | 3 | public class MultipleElementsFoundException extends RuntimeException { 4 | } 5 | -------------------------------------------------------------------------------- /src/main/java/br/com/zbra/androidlinq/delegate/SelectorBigDecimal.java: -------------------------------------------------------------------------------- 1 | package br.com.zbra.androidlinq.delegate; 2 | 3 | import java.math.BigDecimal; 4 | 5 | public interface SelectorBigDecimal extends Selector { 6 | } 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.class 2 | 3 | # Mobile Tools for Java (J2ME) 4 | .mtj.tmp/ 5 | 6 | # Package Files # 7 | *.jar 8 | *.war 9 | *.ear 10 | 11 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 12 | hs_err_pid* 13 | 14 | # IntelliJ 15 | .idea 16 | target/MANIFEST.MF 17 | -------------------------------------------------------------------------------- /src/main/java/br/com/zbra/androidlinq/IterableStream.java: -------------------------------------------------------------------------------- 1 | package br.com.zbra.androidlinq; 2 | 3 | import java.util.Iterator; 4 | 5 | class IterableStream extends AbstractStream { 6 | 7 | private final Iterable iterable; 8 | 9 | public IterableStream(Iterable iterable) { 10 | this.iterable = iterable; 11 | } 12 | 13 | @Override 14 | public Iterator iterator() { 15 | return iterable.iterator(); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/br/com/zbra/androidlinq/ReverseStream.java: -------------------------------------------------------------------------------- 1 | package br.com.zbra.androidlinq; 2 | 3 | import java.util.Iterator; 4 | 5 | class ReverseStream extends AbstractStream { 6 | 7 | private final AbstractStream stream; 8 | 9 | ReverseStream(AbstractStream stream) { 10 | this.stream = stream; 11 | } 12 | 13 | @Override 14 | public Iterator iterator() { 15 | return stream.reverseIterator(); 16 | } 17 | 18 | @Override 19 | protected Iterator reverseIterator() { 20 | return stream.iterator(); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/br/com/zbra/androidlinq/ListStream.java: -------------------------------------------------------------------------------- 1 | package br.com.zbra.androidlinq; 2 | 3 | import java.util.Iterator; 4 | import java.util.List; 5 | 6 | 7 | class ListStream extends AbstractStream { 8 | 9 | private List source; 10 | 11 | ListStream(List source) { 12 | this.source = source; 13 | } 14 | 15 | @Override 16 | public int count() { 17 | return source.size(); 18 | } 19 | 20 | @Override 21 | public Iterator iterator() { 22 | return source.iterator(); 23 | } 24 | 25 | @Override 26 | protected Iterator reverseIterator() { 27 | return new ReverseListIterator<>(source); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/br/com/zbra/androidlinq/MapStream.java: -------------------------------------------------------------------------------- 1 | package br.com.zbra.androidlinq; 2 | 3 | import java.util.Iterator; 4 | import java.util.Map; 5 | 6 | public class MapStream extends AbstractStream> { 7 | 8 | private Map map; 9 | 10 | public MapStream(Map map) { 11 | this.map = map; 12 | } 13 | 14 | @Override 15 | public int count() { 16 | return map.size(); 17 | } 18 | 19 | @Override 20 | public Iterator> iterator() { 21 | return map.entrySet().iterator(); 22 | } 23 | 24 | @Override 25 | protected Iterator> reverseIterator() { 26 | return iterator(); 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/br/com/zbra/androidlinq/ReverseListIterator.java: -------------------------------------------------------------------------------- 1 | package br.com.zbra.androidlinq; 2 | 3 | import java.util.Iterator; 4 | import java.util.List; 5 | import java.util.ListIterator; 6 | 7 | 8 | public class ReverseListIterator implements Iterator { 9 | ListIterator iterator; 10 | public ReverseListIterator(List list) { 11 | iterator = list.listIterator(list.size()); 12 | } 13 | 14 | @Override 15 | public boolean hasNext() { 16 | return iterator.hasPrevious(); 17 | } 18 | 19 | @Override 20 | public T next() { 21 | return iterator.previous(); 22 | } 23 | 24 | @Override 25 | public void remove() { 26 | throw new UnsupportedOperationException("remove"); 27 | } 28 | } -------------------------------------------------------------------------------- /src/main/java/br/com/zbra/androidlinq/Grouping.java: -------------------------------------------------------------------------------- 1 | package br.com.zbra.androidlinq; 2 | 3 | /** 4 | * Represents a collection of TElements that had been grouped through the TKey. 5 | * 6 | * @param type used to group the TElements 7 | * @param type of the elements grouped through the TKey 8 | * @see br.com.zbra.androidlinq.Stream#groupBy(br.com.zbra.androidlinq.delegate.Selector) 9 | * @see br.com.zbra.androidlinq.Stream#groupBy(br.com.zbra.androidlinq.delegate.Selector, br.com.zbra.androidlinq.delegate.Selector) 10 | */ 11 | public interface Grouping { 12 | /** 13 | * @return the key used to perform the grouping. 14 | */ 15 | TKey getKey(); 16 | 17 | /** 18 | * @return a Stream of grouped elements. 19 | */ 20 | Stream getElements(); 21 | } 22 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 ZBRA Solutions 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /android-linq.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /src/main/java/br/com/zbra/androidlinq/SkipStream.java: -------------------------------------------------------------------------------- 1 | package br.com.zbra.androidlinq; 2 | 3 | import java.util.Iterator; 4 | 5 | class SkipStream extends AbstractStream { 6 | 7 | private final int count; 8 | private final AbstractStream stream; 9 | 10 | SkipStream(AbstractStream stream, int count) { 11 | if (count < 0) throw new IllegalArgumentException("count must be greater than 0: " + count); 12 | 13 | this.stream = stream; 14 | this.count = count; 15 | } 16 | 17 | @Override 18 | public int count() { 19 | int originalCount = stream.count(); 20 | return count >= originalCount ? 0 : originalCount - count; 21 | } 22 | 23 | @Override 24 | public Iterator iterator() { 25 | return new SkipIterator<>(stream.iterator(), count); 26 | } 27 | 28 | @Override 29 | protected Iterator reverseIterator() { 30 | return super.reverseIterator(); 31 | } 32 | 33 | private static class SkipIterator implements Iterator { 34 | private final Iterator wrapped; 35 | 36 | public SkipIterator(Iterator wrapped, long count) { 37 | this.wrapped = wrapped; 38 | 39 | for (int i = 0; i < count && wrapped.hasNext(); i++) 40 | wrapped.next(); 41 | } 42 | 43 | @Override 44 | public boolean hasNext() { 45 | return wrapped.hasNext(); 46 | } 47 | 48 | @Override 49 | public T next() { 50 | return wrapped.next(); 51 | } 52 | 53 | @Override 54 | public void remove() { 55 | throw new UnsupportedOperationException("remove"); 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/main/java/br/com/zbra/androidlinq/SelectStream.java: -------------------------------------------------------------------------------- 1 | package br.com.zbra.androidlinq; 2 | 3 | import br.com.zbra.androidlinq.delegate.Selector; 4 | 5 | import java.util.Iterator; 6 | 7 | class SelectStream extends AbstractStream { 8 | private final AbstractStream stream; 9 | private final Selector selector; 10 | 11 | SelectStream(AbstractStream stream, Selector selector) { 12 | this.stream = stream; 13 | this.selector = selector; 14 | } 15 | 16 | @Override 17 | public int count() { 18 | return stream.count(); 19 | } 20 | 21 | @Override 22 | public Iterator iterator() { 23 | return new SelectIterator<>(selector, stream.iterator()); 24 | } 25 | 26 | @Override 27 | protected Iterator reverseIterator() { 28 | return new SelectIterator<>(selector, stream.reverseIterator()); 29 | } 30 | 31 | private static class SelectIterator implements Iterator { 32 | private final Iterator iterator; 33 | private final Selector selector; 34 | 35 | public SelectIterator(Selector selector, Iterator iterator) { 36 | this.iterator = iterator; 37 | this.selector = selector; 38 | } 39 | 40 | @Override 41 | public boolean hasNext() { 42 | return iterator.hasNext(); 43 | } 44 | 45 | @Override 46 | public TSelected next() { 47 | return selector.select(iterator.next()); 48 | } 49 | 50 | @Override 51 | public void remove() { 52 | throw new UnsupportedOperationException("remove"); 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/br/com/zbra/androidlinq/ArrayStream.java: -------------------------------------------------------------------------------- 1 | package br.com.zbra.androidlinq; 2 | 3 | import java.util.Iterator; 4 | import java.util.NoSuchElementException; 5 | 6 | 7 | class ArrayStream extends AbstractStream { 8 | 9 | private final T[] source; 10 | 11 | ArrayStream(T[] source) { 12 | this.source = source; 13 | } 14 | 15 | @Override 16 | public int count() { 17 | return source.length; 18 | } 19 | 20 | @Override 21 | public Iterator iterator() { 22 | return new ArrayIterator<>(source, 0, source.length - 1); 23 | } 24 | 25 | 26 | @Override 27 | protected Iterator reverseIterator() { 28 | return new ArrayIterator<>(source, source.length - 1, 0); 29 | } 30 | 31 | private static class ArrayIterator implements Iterator { 32 | private int index; 33 | private final int increment; 34 | private final int stopOn; 35 | private final T[] source; 36 | 37 | public ArrayIterator(T[] source, int from, int to) { 38 | this.index = from; 39 | this.increment = from < to ? 1 : -1; 40 | this.stopOn = source.length == 0 ? from : to + increment; 41 | this.source = source; 42 | } 43 | 44 | @Override 45 | public boolean hasNext() { 46 | return index != stopOn; 47 | } 48 | 49 | @Override 50 | public T next() { 51 | if (index == stopOn) throw new NoSuchElementException(); 52 | T nextElement = source[index]; 53 | index += increment; 54 | return nextElement; 55 | } 56 | 57 | @Override 58 | public void remove() { 59 | throw new UnsupportedOperationException("remove"); 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/main/java/br/com/zbra/androidlinq/TakeStream.java: -------------------------------------------------------------------------------- 1 | package br.com.zbra.androidlinq; 2 | 3 | import java.util.Iterator; 4 | import java.util.NoSuchElementException; 5 | 6 | class TakeStream extends AbstractStream { 7 | 8 | private final int count; 9 | private final AbstractStream stream; 10 | 11 | TakeStream(AbstractStream stream, int count) { 12 | if (count < 0) throw new IllegalArgumentException("count must be greater than 0: " + count); 13 | 14 | this.stream = stream; 15 | this.count = count; 16 | } 17 | 18 | @Override 19 | public int count() { 20 | return count == 0 ? 0 : Math.min(count, stream.count()); 21 | } 22 | 23 | @Override 24 | public Iterator iterator() { 25 | return new TakeIterator<>(stream.iterator(), count); 26 | } 27 | 28 | @Override 29 | protected Iterator reverseIterator() { 30 | return super.reverseIterator(); 31 | } 32 | 33 | private static class TakeIterator implements Iterator { 34 | private long index = 0; 35 | private final long count; 36 | private final Iterator wrapped; 37 | 38 | public TakeIterator(Iterator wrapped, long count) { 39 | this.count = count; 40 | this.wrapped = wrapped; 41 | } 42 | 43 | @Override 44 | public boolean hasNext() { 45 | return index < count && wrapped.hasNext(); 46 | } 47 | 48 | @Override 49 | public T next() { 50 | index++; 51 | if (index > count) throw new NoSuchElementException(); 52 | return wrapped.next(); 53 | } 54 | 55 | @Override 56 | public void remove() { 57 | throw new UnsupportedOperationException("remove"); 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/main/java/br/com/zbra/androidlinq/WhereStream.java: -------------------------------------------------------------------------------- 1 | package br.com.zbra.androidlinq; 2 | 3 | import br.com.zbra.androidlinq.delegate.Predicate; 4 | 5 | import java.util.Iterator; 6 | import java.util.NoSuchElementException; 7 | 8 | class WhereStream extends AbstractStream { 9 | private final AbstractStream stream; 10 | private final Predicate predicate; 11 | 12 | WhereStream(AbstractStream stream, Predicate predicate) { 13 | this.stream = stream; 14 | this.predicate = predicate; 15 | } 16 | 17 | @Override 18 | public Iterator iterator() { 19 | return new WhereIterator<>(stream.iterator(), predicate); 20 | } 21 | 22 | @Override 23 | protected Iterator reverseIterator() { 24 | return new WhereIterator<>(stream.reverseIterator(), predicate); 25 | } 26 | 27 | private static class WhereIterator implements Iterator { 28 | private final Iterator wrapped; 29 | private final Predicate predicate; 30 | 31 | private T next; 32 | private boolean nextEvaluated; 33 | 34 | public WhereIterator(Iterator wrapped, Predicate predicate) { 35 | this.wrapped = wrapped; 36 | this.predicate = predicate; 37 | } 38 | 39 | @Override 40 | public boolean hasNext() { 41 | evaluateNext(); 42 | return next != null; 43 | } 44 | 45 | @Override 46 | public T next() { 47 | evaluateNext(); 48 | 49 | if (next == null) 50 | throw new NoSuchElementException(); 51 | 52 | nextEvaluated = false; 53 | 54 | return next; 55 | } 56 | 57 | @Override 58 | public void remove() { 59 | throw new UnsupportedOperationException("remove"); 60 | } 61 | 62 | private void evaluateNext() { 63 | if (nextEvaluated) 64 | return; 65 | 66 | next = null; 67 | while (wrapped.hasNext() && next == null) { 68 | T entry = wrapped.next(); 69 | if (predicate.apply(entry)) 70 | next = entry; 71 | } 72 | 73 | nextEvaluated = true; 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/main/java/br/com/zbra/androidlinq/OrderedStream.java: -------------------------------------------------------------------------------- 1 | package br.com.zbra.androidlinq; 2 | 3 | import br.com.zbra.androidlinq.delegate.Comparator; 4 | import br.com.zbra.androidlinq.delegate.Selector; 5 | 6 | public interface OrderedStream extends Stream { 7 | 8 | /** 9 | * Performs a subsequent ordering of the elements in a sequence in ascending order by using a specified comparator. 10 | * 11 | * @param keySelector A function to extract a key from an element. 12 | * @param comparator An Comparator to compare keys. 13 | * @param The type of the key returned by keySelector. 14 | * @return An Stream of type T whose elements are sorted according to a key. 15 | */ 16 | OrderedStream thenBy(Selector keySelector, Comparator comparator); 17 | 18 | /** 19 | * Performs a subsequent ordering of the elements in a sequence in ascending order according to a key. 20 | * 21 | * @param keySelector A function to extract a key from an element. 22 | * @param The type of the key returned by keySelector. 23 | * @return An Stream of type T whose elements are sorted according to a key. 24 | */ 25 | > OrderedStream thenBy(Selector keySelector); 26 | 27 | /** 28 | * Performs a subsequent ordering of the elements in a sequence in descending order by using a specified comparer. 29 | * 30 | * @param keySelector A function to extract a key from an element. 31 | * @param comparator An Comparator to compare keys. 32 | * @param The type of the key returned by keySelector. 33 | * @return An Stream of type T whose elements are sorted according to a key. 34 | */ 35 | OrderedStream thenByDescending(Selector keySelector, Comparator comparator); 36 | 37 | /** 38 | * Performs a subsequent ordering of the elements in a sequence in descending order, according to a key. 39 | * 40 | * @param keySelector A function to extract a key from an element. 41 | * @param The type of the key returned by keySelector. 42 | * @return An Stream of type T whose elements are sorted according to a key. 43 | */ 44 | > OrderedStream thenByDescending(Selector keySelector); 45 | 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/br/com/zbra/androidlinq/Linq.java: -------------------------------------------------------------------------------- 1 | package br.com.zbra.androidlinq; 2 | 3 | import java.util.List; 4 | import java.util.Map; 5 | 6 | /** 7 | * Factory for {@link br.com.zbra.androidlinq.Stream Stream} objects. 8 | */ 9 | public final class Linq { 10 | 11 | private Linq() { 12 | throw new UnsupportedOperationException(); 13 | } 14 | 15 | /** 16 | * Decorates the passed {@code iterable} with a Stream. 17 | * 18 | * @param array a generic typed iterable (usually implementing the {@link java.util.Collection Collection} interface) 19 | * @param the generic type of the {@code array} 20 | * @return a new Stream object that decorates the passed {@code array} 21 | */ 22 | public static Stream stream(T[] array) { 23 | return new ArrayStream<>(array); 24 | } 25 | 26 | /** 27 | * Decorates the passed {@code list} with a Stream. 28 | * 29 | * @param list a generic typed list (usually implementing the {@link java.util.List List} interface) 30 | * @param the generic type of the {@code list} 31 | * @return a new Stream object that decorates the passed {@code list} 32 | */ 33 | public static Stream stream(List list) { 34 | return new ListStream<>(list); 35 | } 36 | 37 | /** 38 | * Decorates the passed {@code iterable} with a Stream. 39 | * 40 | * @param iterable a generic typed iterable (usually implementing the {@link java.util.Collection Collection} interface) 41 | * @param the generic type of the {@code iterable} 42 | * @return a new Stream object that decorates the passed {@code iterable} 43 | */ 44 | public static Stream stream(Iterable iterable) { 45 | return new IterableStream<>(iterable); 46 | } 47 | 48 | /** 49 | * Decorates the passed {@code map} with a Stream. 50 | * 51 | * @param map a generic typed map. 52 | * @param the generic type of the {@code map} key 53 | * @param the generic type for the {@code map} value 54 | * @return a new Stream object that decorates the passed {@code map} entry set 55 | * @see java.util.Map#entrySet() 56 | */ 57 | public static Stream> stream(Map map) { 58 | return new MapStream<>(map); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/main/java/br/com/zbra/androidlinq/SelectManyStream.java: -------------------------------------------------------------------------------- 1 | package br.com.zbra.androidlinq; 2 | 3 | import br.com.zbra.androidlinq.delegate.Selector; 4 | 5 | import java.util.Iterator; 6 | import java.util.NoSuchElementException; 7 | 8 | class SelectManyStream extends AbstractStream { 9 | 10 | private final AbstractStream stream; 11 | private final Selector> selector; 12 | 13 | SelectManyStream(AbstractStream stream, Selector> selector) { 14 | this.stream = stream; 15 | this.selector = selector; 16 | } 17 | 18 | @Override 19 | public Iterator iterator() { 20 | return new SelectManyIterator<>(selector, stream.iterator()); 21 | } 22 | 23 | @Override 24 | protected Iterator reverseIterator() { 25 | return super.reverseIterator(); 26 | } 27 | 28 | private static class SelectManyIterator implements Iterator { 29 | private Boolean hasNext; 30 | private Iterator selectedIterator; 31 | private final Iterator sourceIterator; 32 | private final Selector> selector; 33 | 34 | public SelectManyIterator(Selector> selector, Iterator sourceIterator) { 35 | this.sourceIterator = sourceIterator; 36 | this.selector = selector; 37 | } 38 | 39 | @Override 40 | public boolean hasNext() { 41 | if (hasNext != null) 42 | return hasNext; 43 | 44 | if (selectedIterator != null && selectedIterator.hasNext()) 45 | return hasNext = true; 46 | 47 | while(sourceIterator.hasNext()) { 48 | Iterable selectedIterable = selector.select(sourceIterator.next()); 49 | if ((selectedIterator = selectedIterable.iterator()).hasNext()) 50 | return hasNext = true; 51 | } 52 | 53 | return hasNext = false; 54 | } 55 | 56 | @Override 57 | public TSelected next() { 58 | if (!hasNext()) throw new NoSuchElementException(); 59 | hasNext = null; 60 | return selectedIterator.next(); 61 | } 62 | 63 | @Override 64 | public void remove() { 65 | throw new UnsupportedOperationException("remove"); 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/main/java/br/com/zbra/androidlinq/GroupByStream.java: -------------------------------------------------------------------------------- 1 | package br.com.zbra.androidlinq; 2 | 3 | import br.com.zbra.androidlinq.delegate.Selector; 4 | 5 | import java.util.ArrayList; 6 | import java.util.HashMap; 7 | import java.util.Iterator; 8 | import java.util.List; 9 | 10 | import static br.com.zbra.androidlinq.Linq.stream; 11 | 12 | 13 | class GroupByStream extends AbstractStream> { 14 | 15 | private final AbstractStream stream; 16 | private final Selector keySelector; 17 | private final Selector elementSelector; 18 | 19 | GroupByStream(AbstractStream stream, Selector keySelector, Selector elementSelector) { 20 | this.stream = stream; 21 | this.keySelector = keySelector; 22 | this.elementSelector = elementSelector; 23 | } 24 | 25 | @Override 26 | public Iterator> iterator() { 27 | return getGroupingIterator(stream.iterator()); 28 | } 29 | 30 | @Override 31 | protected Iterator> reverseIterator() { 32 | return super.reverseIterator(); 33 | } 34 | 35 | private Iterator> getGroupingIterator(Iterator iterator) { 36 | HashMap> map = new HashMap<>(); 37 | List> groupings = new ArrayList<>(); 38 | 39 | while (iterator.hasNext()) { 40 | T t = iterator.next(); 41 | TKey key = keySelector.select(t); 42 | TElement element = elementSelector.select(t); 43 | 44 | GroupingImpl grouping = map.get(key); 45 | if (grouping == null) { 46 | grouping = new GroupingImpl<>(key); 47 | map.put(key, grouping); 48 | groupings.add(grouping); 49 | } 50 | 51 | grouping.source.add(element); 52 | } 53 | 54 | return groupings.iterator(); 55 | } 56 | 57 | private static class GroupingImpl implements Grouping { 58 | private final TKey key; 59 | private final List source; 60 | 61 | private GroupingImpl(TKey key) { 62 | this.key = key; 63 | this.source = new ArrayList<>(); 64 | } 65 | 66 | public TKey getKey() { 67 | return key; 68 | } 69 | 70 | public Stream getElements() { 71 | return stream(source); 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/test/groovy/br/com/zbra/androidlinq/LinqTest.groovy: -------------------------------------------------------------------------------- 1 | package br.com.zbra.androidlinq 2 | 3 | import java.lang.reflect.Constructor 4 | 5 | import static br.com.zbra.androidlinq.Linq.stream 6 | 7 | @SuppressWarnings("GroovyUnusedDeclaration") 8 | class LinqTest extends GroovyTestCase { 9 | 10 | void testConstructor() { 11 | Constructor constructor = Linq.class.getDeclaredConstructor() 12 | constructor.setAccessible(true) 13 | shouldFailWithCause(UnsupportedOperationException.class, { 14 | constructor.newInstance() 15 | }) 16 | } 17 | 18 | void testArrayStream() { 19 | def array = 0..9 as int[] 20 | def arrayStream = stream(array) 21 | 22 | assert arrayStream.count() == array.length 23 | assert arrayStream.toList().toArray() as int[] == array 24 | assert arrayStream.reverse().toList().toArray() as int[] == 9..0 as int[] 25 | 26 | assert stream(new int[0]).toList() == [] 27 | assert stream(new int[0]).reverse().toList() == [] 28 | } 29 | 30 | void testListStream() { 31 | def list = 0..9 as List 32 | def listStream = stream(list) 33 | 34 | assert listStream.count() == list.size() 35 | assert listStream.toList() == list 36 | assert listStream.reverse().toList() == 9..0 as List 37 | 38 | assert stream(new ArrayList()).toList() == [] 39 | assert stream(new ArrayList()).reverse().toList() == [] 40 | } 41 | 42 | void testIterableStream() { 43 | def iterable = 0..9 as Set 44 | def iterableStream = stream(iterable) 45 | 46 | assert iterableStream.count() == iterable.size() 47 | assert iterableStream.toList() == iterable.toList() 48 | assert iterableStream.reverse().toList() == 9..0 as List 49 | 50 | assert stream(new HashSet()).toList() == [] 51 | assert stream(new HashSet()).reverse().toList() == [] 52 | } 53 | 54 | void testMapStream() { 55 | 56 | def map = [ 1: "One", 2: "Two", 3: "Three", 4: "Four", 5: "Five"] 57 | def entrySetStream = stream(map) 58 | 59 | assert entrySetStream.select({ n -> n.key }).toList() == 1..5 60 | assert entrySetStream.select({ n -> n.value }).toList() == [ "One", "Two", "Three", "Four", "Five" ] 61 | assert entrySetStream.toMap({n -> n.key }, { n -> n.value }) == map 62 | 63 | assert stream(new HashMap()).toList() == [] 64 | assert stream(new HashMap()).reverse().toList() == [] 65 | 66 | assert stream(map).count() == map.size() 67 | assert stream(new HashMap()).count() == 0 68 | 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 4.0.0 7 | 8 | br.com.zbra 9 | android-linq 10 | 1.1.0 11 | jar 12 | 13 | 14 | 15 | junit 16 | junit 17 | RELEASE 18 | test 19 | 20 | 21 | 22 | 23 | 1.7 24 | 1.7 25 | 26 | 27 | Android LINQ 28 | http://zbra-solutions.github.io/android-linq/ 29 | Manipulate collections easily using C# LINQ style queries and Java 8 closures. 30 | 31 | 32 | 33 | MIT License 34 | http://www.opensource.org/licenses/mit-license.php 35 | repo 36 | 37 | 38 | 39 | 40 | 41 | brunovinicius 42 | Bruno Vinicius 43 | bruno.vinicius.ds@gmail.com 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | org.apache.maven.plugins 52 | maven-source-plugin 53 | 2.0.4 54 | 55 | 56 | org.apache.maven.plugins 57 | maven-javadoc-plugin 58 | 2.3 59 | 60 | 61 | 62 | 63 | 64 | org.apache.maven.plugins 65 | maven-source-plugin 66 | 67 | 68 | attach-sources 69 | 70 | jar 71 | 72 | 73 | 74 | 75 | 76 | org.apache.maven.plugins 77 | maven-javadoc-plugin 78 | 79 | 80 | attach-javadocs 81 | 82 | jar 83 | 84 | 85 | 86 | 87 | 88 | 89 | -------------------------------------------------------------------------------- /src/main/java/br/com/zbra/androidlinq/OrderByStream.java: -------------------------------------------------------------------------------- 1 | package br.com.zbra.androidlinq; 2 | 3 | import br.com.zbra.androidlinq.delegate.Comparator; 4 | import br.com.zbra.androidlinq.delegate.Selector; 5 | 6 | import java.util.ArrayList; 7 | import java.util.Collections; 8 | import java.util.Iterator; 9 | import java.util.List; 10 | 11 | class OrderByStream extends AbstractStream implements OrderedStream { 12 | 13 | private final Stream stream; 14 | private final QueuedComparators queuedComparators; 15 | 16 | static OrderedStream createAscending(Stream stream, Selector selector, Comparator comparator) { 17 | OrderByStream orderByStream = new OrderByStream<>(stream); 18 | orderByStream.thenBy(selector, comparator); 19 | return orderByStream; 20 | } 21 | 22 | static OrderedStream createDescending(Stream stream, Selector selector, Comparator comparator) { 23 | OrderByStream orderByStream = new OrderByStream<>(stream); 24 | orderByStream.thenByDescending(selector, comparator); 25 | return orderByStream; 26 | } 27 | 28 | private OrderByStream(Stream stream) { 29 | this.stream = stream; 30 | this.queuedComparators = new QueuedComparators<>(); 31 | } 32 | 33 | @Override 34 | public int count() { 35 | return stream.count(); 36 | } 37 | 38 | @Override 39 | public Iterator iterator() { 40 | return getIterator(queuedComparators); 41 | } 42 | 43 | @Override 44 | protected Iterator reverseIterator() { 45 | return getIterator(Collections.reverseOrder(queuedComparators)); 46 | } 47 | 48 | private Iterator getIterator(java.util.Comparator comparator) { 49 | List list = stream.toList(); 50 | Collections.sort(list, comparator); 51 | return list.iterator(); 52 | } 53 | 54 | @Override 55 | public OrderedStream thenBy(final Selector keySelector, final Comparator comparator) { 56 | this.queuedComparators.addComparator(new java.util.Comparator() { 57 | @Override 58 | public int compare(T t1, T t2) { 59 | return comparator.compare(keySelector.select(t1), keySelector.select(t2)); 60 | } 61 | }); 62 | return this; 63 | } 64 | 65 | @Override 66 | public > OrderedStream thenBy(Selector keySelector) { 67 | thenBy(keySelector, new Comparator() { 68 | @Override 69 | public int compare(TKey tKey, TKey o) { 70 | return tKey.compareTo(o); 71 | } 72 | }); 73 | return this; 74 | } 75 | 76 | @Override 77 | public OrderedStream thenByDescending(final Selector keySelector, final Comparator comparator) { 78 | this.queuedComparators.addComparator(new java.util.Comparator() { 79 | @Override 80 | public int compare(T t1, T t2) { 81 | return comparator.compare(keySelector.select(t1), keySelector.select(t2)) * -1; 82 | } 83 | }); 84 | return this; 85 | } 86 | 87 | @Override 88 | public > OrderedStream thenByDescending(Selector keySelector) { 89 | thenByDescending(keySelector, new Comparator() { 90 | @Override 91 | public int compare(TKey tKey, TKey o) { 92 | return tKey.compareTo(o); 93 | } 94 | }); 95 | return this; 96 | } 97 | 98 | private static class QueuedComparators implements java.util.Comparator { 99 | private List> comparators = new ArrayList<>(); 100 | 101 | @Override 102 | public int compare(T o1, T o2) { 103 | int size = comparators.size(); 104 | int compare = 0; 105 | for (int i = 0; i < size && compare == 0; i++) { 106 | compare = comparators.get(i).compare(o1, o2); 107 | } 108 | return compare; 109 | } 110 | 111 | public void addComparator(java.util.Comparator comparator) { 112 | comparators.add(comparator); 113 | } 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Android LINQ 2 | 3 | Manipulate collections easily using C# LINQ style queries and Java 8 closures. 4 | 5 | ## Description 6 | 7 | Android LINQ is a small subset of collection manipulation utilities inspired by Microsoft C# LINQ library and targeted at Android developers looking to use new Java 8 Stream() API. 8 | 9 | By using [Retrolambda for Android](https://github.com/evant/gradle-retrolambda), developers can leverage the power of closures and other new Java 8 features. Unfortunately, it doesn't allow the usage of the Stream API which is arguably its most awesome feature. However, by using it in conjunction with Android LINQ, its possible to perform powerful collection manipulation in just a few lines of code. 10 | 11 | Android LINQ has little to no impact on performance because it does not make use of reflection or proxies. As its C# counterpart it's based on the [monads](http://en.wikipedia.org/wiki/Monad_(functional_programming)) concept, which is a fancy word to describe a sort of [Decorator](http://en.wikipedia.org/wiki/Decorator_pattern) pattern implementation, and many sorting and ordering are just making calls to the default Java API. 12 | 13 | Anyway, you need not to worry. Just add this to your Gradle/Maven and suffer with manual collection iteration no more! 14 | 15 | ## Usage 16 | 17 | ### Latest Version 18 | 19 | [ ![Download](https://api.bintray.com/packages/brunovinicius/maven/android-linq/images/download.svg) ](https://bintray.com/brunovinicius/maven/android-linq/_latestVersion) 20 | 21 | ### Android 22 | 23 | To use Android LINQ, first, go and setup [Retrolambda for Android](https://github.com/evant/gradle-retrolambda) so we can use those fancy closures from Java 8 (don't worry, its just some extra lines on your build.gradle file). 24 | 25 | Now, just add this line to your project build.gradle (files are hosted in Bintray jCenter, so don't forget to add it to the repositories list too). 26 | 27 | #### Gradle 28 | 29 | #### Android Studio >= 4.0 30 | 31 | Android Studio 4.0 introduced a ne version of Gradle and with that, usage changed a little. See https://jitpack.io/#zbra-solutions/android-linq/1.1.0 for more details. 32 | 33 | ``` 34 | ... 35 | allprojects { 36 | repositories { 37 | ... 38 | maven { url 'https://jitpack.io' } 39 | } 40 | } 41 | ... 42 | ``` 43 | ``` 44 | dependencies { 45 | implementation 'com.github.zbra-solutions:android-linq:1.1.0' 46 | } 47 | ``` 48 | 49 | #### Legacy 50 | ``` 51 | ... 52 | repositories { 53 | jcenter() 54 | } 55 | ... 56 | ``` 57 | ``` 58 | compile 'br.com.zbra:android-linq:1.1.0' 59 | ``` 60 | 61 | ### Standard Java 8 62 | 63 | Android LINQ uses standard Java and therefore can also be used outside Android. 64 | 65 | #### Maven 66 | 67 | ``` 68 | 69 | ... 70 | 71 | jcenter 72 | http://jcenter.bintray.com 73 | ... 74 | 75 | ... 76 | 77 | ``` 78 | ``` 79 | 80 | br.com.zbra 81 | android-linq 82 | 1.1.0 83 | 84 | ``` 85 | 86 | ## Examples 87 | 88 | #### Get names from contacts 89 | ``` 90 | List contactNames = 91 | stream(contacts) 92 | .select(c -> c.getName()) 93 | .toList(); 94 | ```` 95 | #### Get contacts who are 27 years or older 96 | ``` 97 | List contacts = 98 | stream(contacts) 99 | .where(c -> c.getAge() >= 27) 100 | .toList(); 101 | ``` 102 | #### Sort contacts by name, then by age 103 | ``` 104 | List contactNames = 105 | stream(contacts) 106 | .orderBy(c -> c.getName()) 107 | .thenBy(c -> c.Age()) 108 | .toList(); 109 | ``` 110 | #### Group products by category 111 | ``` 112 | Map> productsByCategory 113 | stream(products) 114 | .groupBy(p -> p.getCategory()) 115 | .toMap(g -> g.getKey() /* Category */, g.getElements() /* Stream */) 116 | ``` 117 | #### Calculate the total price of a purchase 118 | ``` 119 | double total = 120 | stream(purchase.getItems()) 121 | .sum((Purchase p) -> p.getPrice()); 122 | ``` 123 | 124 | There are many more methods: first(), single(), distinct(), any(), aggregate(), count(), take(), skip() and reverse() are all available. Have fun! 125 | 126 | ## Pull Requests Are Welcome! 127 | 128 | Please, send feedback and pull requests a plenty! 129 | If you find a bug, a missing feature or have an improvement suggestion, don't be afraid to file an issue and we will do our best to attend it. 130 | -------------------------------------------------------------------------------- /src/main/java/br/com/zbra/androidlinq/AbstractStream.java: -------------------------------------------------------------------------------- 1 | package br.com.zbra.androidlinq; 2 | 3 | import br.com.zbra.androidlinq.delegate.*; 4 | import br.com.zbra.androidlinq.delegate.Comparator; 5 | import br.com.zbra.androidlinq.exception.MultipleElementsFoundException; 6 | 7 | import java.math.BigDecimal; 8 | import java.math.MathContext; 9 | import java.util.*; 10 | 11 | abstract class AbstractStream implements Stream { 12 | 13 | protected Iterator reverseIterator() { 14 | Deque deque = new LinkedList<>(); 15 | for (T t : this) deque.addFirst(t); 16 | return deque.iterator(); 17 | } 18 | 19 | @Override 20 | public Stream where(Predicate predicate) { 21 | return new WhereStream<>(this, predicate); 22 | } 23 | 24 | @Override 25 | public Stream select(Selector selector) { 26 | return new SelectStream<>(this, selector); 27 | } 28 | 29 | @Override 30 | public Stream selectMany(Selector> selector) { 31 | return new SelectManyStream<>(this, selector); 32 | } 33 | 34 | @Override 35 | public Stream> groupBy(Selector keySelector, Selector elementSelector) { 36 | return new GroupByStream<>(this, keySelector, elementSelector); 37 | } 38 | 39 | @Override 40 | public Stream> groupBy(Selector keySelector) { 41 | return groupBy(keySelector, new Selector() { 42 | @Override 43 | public T select(T t) { 44 | return t; 45 | } 46 | }); 47 | } 48 | 49 | @Override 50 | public OrderedStream orderBy(Selector keySelector, Comparator comparator) { 51 | return OrderByStream.createAscending(this, keySelector, comparator); 52 | } 53 | 54 | @Override 55 | public > OrderedStream orderBy(Selector keySelector) { 56 | return orderBy(keySelector, new Comparator() { 57 | @Override 58 | public int compare(R r, R o) { 59 | return r.compareTo(o); 60 | } 61 | }); 62 | } 63 | 64 | @Override 65 | public OrderedStream orderByDescending(Selector keySelector, Comparator comparator) { 66 | return OrderByStream.createDescending(this, keySelector, comparator); 67 | } 68 | 69 | @Override 70 | public > OrderedStream orderByDescending(final Selector keySelector) { 71 | return orderByDescending(keySelector, new Comparator() { 72 | @Override 73 | public int compare(R r, R o) { 74 | return r.compareTo(o); 75 | } 76 | }); 77 | } 78 | 79 | @Override 80 | public Stream reverse() { 81 | return new ReverseStream<>(this); 82 | } 83 | 84 | @Override 85 | public R aggregate(R seed, Aggregator aggregator) { 86 | for (T t : this) 87 | seed = aggregator.aggregate(seed, t); 88 | return seed; 89 | } 90 | 91 | @Override 92 | public Stream take(int count) { 93 | return new TakeStream<>(this, count); 94 | } 95 | 96 | @Override 97 | public Stream skip(int count) { 98 | return new SkipStream<>(this, count); 99 | } 100 | 101 | @Override 102 | public Stream distinct() { 103 | final HashSet set = new HashSet<>(); 104 | return where(new Predicate() { 105 | @Override 106 | public boolean apply(T e) { 107 | return set.add(e); 108 | } 109 | }); 110 | } 111 | 112 | @Override 113 | public Byte sum(final SelectorByte selector) { 114 | return aggregate((byte)0, new Aggregator() { 115 | @Override 116 | public Byte aggregate(Byte v, T t) { 117 | return (byte) (v + selector.select(t)); 118 | } 119 | }); 120 | } 121 | 122 | @Override 123 | public Short sum(final SelectorShort selector) { 124 | return aggregate((short)0, new Aggregator() { 125 | @Override 126 | public Short aggregate(Short v, T t) { 127 | return (short) (v + selector.select(t)); 128 | } 129 | }); 130 | } 131 | 132 | @Override 133 | public Integer sum(final SelectorInteger selector) { 134 | return aggregate(0, new Aggregator() { 135 | @Override 136 | public Integer aggregate(Integer v, T t) { 137 | return v + selector.select(t); 138 | } 139 | }); 140 | } 141 | 142 | @Override 143 | public Long sum(final SelectorLong selector) { 144 | return aggregate(0L, new Aggregator() { 145 | @Override 146 | public Long aggregate(Long v, T t) { 147 | return v + selector.select(t); 148 | } 149 | }); 150 | } 151 | 152 | @Override 153 | public Float sum(final SelectorFloat selector) { 154 | return aggregate(0f, new Aggregator() { 155 | @Override 156 | public Float aggregate(Float v, T t) { 157 | return v + selector.select(t); 158 | } 159 | }); 160 | } 161 | 162 | @Override 163 | public Double sum(final SelectorDouble selector) { 164 | return aggregate(0d, new Aggregator() { 165 | @Override 166 | public Double aggregate(Double v, T t) { 167 | return v + selector.select(t); 168 | } 169 | }); 170 | } 171 | 172 | @Override 173 | public BigDecimal sum(final SelectorBigDecimal selector) { 174 | return aggregate(new BigDecimal(0), new Aggregator() { 175 | @Override 176 | public BigDecimal aggregate(BigDecimal v, T t) { 177 | return v.add(selector.select(t)); 178 | } 179 | }); 180 | } 181 | 182 | @Override 183 | public Byte average(SelectorByte selector) { 184 | return (byte)(sum(selector) / count()); 185 | } 186 | 187 | @Override 188 | public Short average(SelectorShort selector) { 189 | return (short)(sum(selector) / count()); 190 | } 191 | 192 | @Override 193 | public Integer average(SelectorInteger selector) { 194 | return sum(selector) / count(); 195 | } 196 | 197 | @Override 198 | public Long average(SelectorLong selector) { 199 | return sum(selector) / count(); 200 | } 201 | 202 | @Override 203 | public Float average(SelectorFloat selector) { 204 | return sum(selector) / count(); 205 | } 206 | 207 | @Override 208 | public Double average(SelectorDouble selector) { 209 | return sum(selector) / count(); 210 | } 211 | 212 | @Override 213 | public BigDecimal average(SelectorBigDecimal selector) { 214 | //noinspection BigDecimalMethodWithoutRoundingCalled 215 | return sum(selector).divide(new BigDecimal(count())); 216 | } 217 | 218 | @Override 219 | public BigDecimal average(SelectorBigDecimal selector, MathContext mathContext) { 220 | return sum(selector).divide(new BigDecimal(count()), mathContext); 221 | } 222 | 223 | @Override 224 | public > T min(Selector selector) { 225 | return orderBy(selector).first(); 226 | } 227 | 228 | @Override 229 | public T min(Selector selector, Comparator comparator) { 230 | return orderBy(selector, comparator).first(); 231 | } 232 | 233 | @Override 234 | public > T max(Selector selector) { 235 | return orderBy(selector).last(); 236 | } 237 | 238 | @Override 239 | public T max(Selector selector, Comparator comparator) { 240 | return orderBy(selector, comparator).last(); 241 | } 242 | 243 | @Override 244 | public boolean any() { 245 | return iterator().hasNext(); 246 | } 247 | 248 | @Override 249 | public boolean any(Predicate predicate) { 250 | return where(predicate).any(); 251 | } 252 | 253 | @Override 254 | public boolean contains(final T element) { 255 | return contains(element, new EqualityComparator() { 256 | @Override 257 | public boolean compare(T value1, T value2) { 258 | return value1.equals(value2); 259 | } 260 | }); 261 | } 262 | 263 | @Override 264 | public boolean contains(final T element, final EqualityComparator comparator) { 265 | return any(new Predicate() { 266 | @Override 267 | public boolean apply(T value) { 268 | return comparator.compare(value, element); 269 | } 270 | }); 271 | } 272 | 273 | @Override 274 | public int count() { 275 | return aggregate(0, new Aggregator() { 276 | @Override 277 | public Integer aggregate(Integer a, T t) { 278 | return a + 1; 279 | } 280 | }); 281 | } 282 | 283 | @Override 284 | public T first() { 285 | return iterator().next(); 286 | } 287 | 288 | @Override 289 | public T first(Predicate predicate) { 290 | return where(predicate).first(); 291 | } 292 | 293 | @Override 294 | public T firstOrNull() { 295 | return firstOrDefault(null); 296 | } 297 | 298 | @Override 299 | public T firstOrNull(Predicate predicate) { 300 | return firstOrDefault(predicate, null); 301 | } 302 | 303 | @Override 304 | public T firstOrDefault(T defaultValue) { 305 | Iterator iterator = iterator(); 306 | return iterator.hasNext() ? iterator.next() : defaultValue; 307 | } 308 | 309 | @Override 310 | public T firstOrDefault(Predicate predicate, T defaultValue) { 311 | return where(predicate).firstOrDefault(defaultValue); 312 | } 313 | 314 | @Override 315 | public T last() { 316 | return reverse().first(); 317 | } 318 | 319 | @Override 320 | public T last(Predicate predicate) { 321 | return reverse().first(predicate); 322 | } 323 | 324 | @Override 325 | public T lastOrNull() { 326 | return lastOrDefault(null); 327 | } 328 | 329 | @Override 330 | public T lastOrNull(Predicate predicate) { 331 | return lastOrDefault(predicate, null); 332 | } 333 | 334 | @Override 335 | public T lastOrDefault(T defaultValue) { 336 | return reverse().firstOrDefault(defaultValue); 337 | } 338 | 339 | @Override 340 | public T lastOrDefault(Predicate predicate, T defaultValue) { 341 | return reverse().firstOrDefault(predicate, defaultValue); 342 | } 343 | 344 | @Override 345 | public T single() throws MultipleElementsFoundException { 346 | Iterator iterator = iterator(); 347 | T result = iterator.next(); 348 | if (iterator.hasNext()) 349 | throw new MultipleElementsFoundException(); 350 | return result; 351 | } 352 | 353 | @Override 354 | public T single(Predicate predicate) throws MultipleElementsFoundException { 355 | return where(predicate).single(); 356 | } 357 | 358 | @Override 359 | public T singleOrNull() { 360 | return singleOrDefault(null); 361 | } 362 | 363 | @Override 364 | public T singleOrNull(Predicate predicate) { 365 | return singleOrDefault(predicate, null); 366 | } 367 | 368 | @Override 369 | public T singleOrDefault(T defaultValue) { 370 | Iterator iterator = iterator(); 371 | if (!iterator.hasNext()) 372 | return defaultValue; 373 | T result = iterator.next(); 374 | if (iterator.hasNext()) 375 | throw new MultipleElementsFoundException(); 376 | return result; 377 | } 378 | 379 | @Override 380 | public T singleOrDefault(Predicate predicate, T defaultValue) { 381 | return where(predicate).singleOrDefault(defaultValue); 382 | } 383 | 384 | @Override 385 | public List toList() { 386 | List list = new ArrayList<>(); 387 | for (T entry : this) 388 | list.add(entry); 389 | return list; 390 | } 391 | 392 | @Override 393 | public Map toMap(Selector keySelector) { 394 | return toMap(keySelector, new Selector() { 395 | @Override 396 | public T select(T t) { 397 | return t; 398 | } 399 | }); 400 | } 401 | 402 | @Override 403 | public Map toMap(Selector keySelector, Selector valueSelector) { 404 | Map map = new HashMap<>(); 405 | for (T entry : this) 406 | map.put(keySelector.select(entry), valueSelector.select(entry)); 407 | return map; 408 | } 409 | } 410 | -------------------------------------------------------------------------------- /src/test/groovy/br/com/zbra/androidlinq/StreamTest.groovy: -------------------------------------------------------------------------------- 1 | package br.com.zbra.androidlinq 2 | 3 | import br.com.zbra.androidlinq.delegate.* 4 | import br.com.zbra.androidlinq.exception.MultipleElementsFoundException 5 | 6 | import java.math.MathContext 7 | import java.math.RoundingMode 8 | 9 | import static br.com.zbra.androidlinq.Linq.stream as stream 10 | 11 | @SuppressWarnings("GroovyUnusedDeclaration") 12 | class StreamTest extends GroovyTestCase { 13 | 14 | void testWhere() { 15 | def stream = stream(0..9).where({ int n -> n % 2 == 0 }) 16 | 17 | assert [0, 2, 4, 6, 8] == stream.toList() 18 | assert [8, 6, 4, 2, 0] == stream.reverse().toList() 19 | } 20 | 21 | void testSelect() { 22 | def integers = 0..9 23 | def stream = stream(integers).select({ n -> n * 2 }) 24 | 25 | assert integers.collect { it * 2 } == stream.toList() 26 | assert integers.reverse().collect { it * 2 } == stream.reverse().toList() 27 | 28 | assert integers.size() == stream.count() 29 | } 30 | 31 | void testSelectMany() { 32 | assert 1..9 == 33 | stream([1..3, [4], 5..6, [7], 8..9]) 34 | .selectMany({ c -> c }) 35 | .toList() 36 | 37 | assert [9, 8, 7, 6, 5, 4, 3, 2, 1] == 38 | stream([1..3, [4], 5..6, [7], 8..9]) 39 | .selectMany({ c -> c }) 40 | .reverse() 41 | .toList() 42 | 43 | assert [] == 44 | stream([[], [], [], [], [], [], []]) 45 | .selectMany({ n -> n }) 46 | .toList() 47 | 48 | assert [null, null, null] == 49 | stream([[null], [null], [null]]) 50 | .selectMany({ n -> n }) 51 | .toList() 52 | 53 | shouldFail(NullPointerException.class, { 54 | stream([[]]) 55 | .selectMany({ n -> null }) 56 | .toList() 57 | }) 58 | } 59 | 60 | void testGroupBy() { 61 | def result 62 | def integers = 0..9 63 | def keySelector = { int n -> n % 2 == 0 ? "Even" : "Odd" } as Selector 64 | def elementSelector = { int n -> n * 10 } as Selector 65 | 66 | // groupBy with keySelector only 67 | result = stream(integers) 68 | .groupBy(keySelector) 69 | .toList() 70 | 71 | assert result.size() == 2 72 | 73 | assert result.get(0).key == "Even" 74 | assert result.get(0).elements.toList() == [0, 2, 4, 6, 8] 75 | 76 | assert result.get(1).key == "Odd" 77 | assert result.get(1).elements.toList() == [1, 3, 5, 7, 9] 78 | 79 | // groupBy with key and element selectors 80 | def groupByStream = stream(integers) 81 | .groupBy(keySelector, elementSelector) 82 | 83 | result = groupByStream.toList() 84 | 85 | assert result.size() == 2 86 | 87 | assert result.get(0).key == "Even" 88 | assert result.get(0).elements.toList() == [00, 20, 40, 60, 80] 89 | 90 | assert result.get(1).key == "Odd" 91 | assert result.get(1).elements.toList() == [10, 30, 50, 70, 90] 92 | 93 | // test reverse 94 | def reverseResult = groupByStream.reverse().toList() 95 | assert reverseResult.get(0).key == "Odd" 96 | assert reverseResult.get(0).elements.toList() == [10, 30, 50, 70, 90] 97 | 98 | assert reverseResult.get(1).key == "Even" 99 | assert reverseResult.get(1).elements.toList() == [00, 20, 40, 60, 80] 100 | } 101 | 102 | @SuppressWarnings("GroovyAssignabilityCheck") 103 | void testOrderBy() { 104 | def integers = 0..9 105 | def integersDescending = 9..0 106 | def numbersComparator = { n1, n2 -> n1 - n2 } 107 | def shuffledItems = [] 108 | 109 | shuffledItems.addAll(integers) 110 | Collections.shuffle(shuffledItems) 111 | 112 | // orderBy ascending 113 | assert integers == 114 | stream(shuffledItems) 115 | .orderBy({ n -> n }) 116 | .toList() 117 | 118 | // orderBy ascending with comparator 119 | assert integers == 120 | stream(shuffledItems) 121 | .orderBy({ n -> n }, numbersComparator) 122 | .toList() 123 | 124 | // orderBy count() 125 | assert shuffledItems.size() == 126 | stream(shuffledItems) 127 | .orderBy({ n -> n }, numbersComparator) 128 | .count() 129 | 130 | // orderBy descending 131 | assert integersDescending == 132 | stream(shuffledItems) 133 | .orderByDescending({ n -> n }) 134 | .toList() 135 | 136 | // orderBy descending with comparator 137 | assert integersDescending == 138 | stream(shuffledItems) 139 | .orderByDescending({ n -> n }, numbersComparator) 140 | .toList() 141 | 142 | // reverse cohesion 143 | assert stream(shuffledItems).orderBy({ n -> n }).toList() == 144 | stream(shuffledItems).orderByDescending({ n -> n }).reverse().toList() 145 | 146 | assert stream(shuffledItems).orderByDescending({ n -> n }).toList() == 147 | stream(shuffledItems).orderBy({ n -> n }).reverse().toList() 148 | 149 | // thenBy & thenByDescending over orderBy 150 | def matrix = [[1, 1], [1, 2], [1, 3], [2, 1], [2, 2], [2, 3], [3, 1], [3, 2], [3, 3]] 151 | def shuffledMatrix = [] 152 | 153 | shuffledMatrix.addAll(matrix) 154 | Collections.shuffle(shuffledMatrix) 155 | 156 | assert [[1, 1], [1, 2], [1, 3], [2, 1], [2, 2], [2, 3], [3, 1], [3, 2], [3, 3]] == 157 | stream(shuffledMatrix) 158 | .orderBy({ n -> n[0] }) 159 | .thenBy({ n -> n[1] }) 160 | .toList() 161 | 162 | assert [[1, 3], [1, 2], [1, 1], [2, 3], [2, 2], [2, 1], [3, 3], [3, 2], [3, 1]] == 163 | stream(shuffledMatrix) 164 | .orderBy({ n -> n[0] }) 165 | .thenByDescending({ n -> n[1] }) 166 | .toList() 167 | 168 | 169 | assert [[1, 1], [1, 2], [1, 3], [2, 1], [2, 2], [2, 3], [3, 1], [3, 2], [3, 3]] == 170 | stream(shuffledMatrix) 171 | .orderBy({ n -> n[0] }) 172 | .thenBy({ n -> n[1] }, numbersComparator) 173 | .toList() 174 | 175 | assert [[1, 3], [1, 2], [1, 1], [2, 3], [2, 2], [2, 1], [3, 3], [3, 2], [3, 1]] == 176 | stream(shuffledMatrix) 177 | .orderBy({ n -> n[0] }) 178 | .thenByDescending({ n -> n[1] }, numbersComparator) 179 | .toList() 180 | 181 | // thenBy & thenByDescending over orderByDescending 182 | assert [[3, 1], [3, 2], [3, 3], [2, 1], [2, 2], [2, 3], [1, 1], [1, 2], [1, 3]] == 183 | stream(shuffledMatrix) 184 | .orderByDescending({ n -> n[0] }) 185 | .thenBy({ n -> n[1] }) 186 | .toList() 187 | 188 | assert [[3, 3], [3, 2], [3, 1], [2, 3], [2, 2], [2, 1], [1, 3], [1, 2], [1, 1]] == 189 | stream(shuffledMatrix) 190 | .orderByDescending({ n -> n[0] }) 191 | .thenByDescending({ n -> n[1] }) 192 | .toList() 193 | 194 | 195 | assert [[3, 1], [3, 2], [3, 3], [2, 1], [2, 2], [2, 3], [1, 1], [1, 2], [1, 3]] == 196 | stream(shuffledMatrix) 197 | .orderByDescending({ n -> n[0] }) 198 | .thenBy({ n -> n[1] }, numbersComparator) 199 | .toList() 200 | 201 | assert [[3, 3], [3, 2], [3, 1], [2, 3], [2, 2], [2, 1], [1, 3], [1, 2], [1, 1]] == 202 | stream(shuffledMatrix) 203 | .orderByDescending({ n -> n[0] }) 204 | .thenByDescending({ n -> n[1] }, numbersComparator) 205 | .toList() 206 | } 207 | 208 | void testAggregate() { 209 | def integers = 0..9 210 | assert integers.sum() == 211 | stream(integers).aggregate(0, { accumulate, n -> accumulate + n }) 212 | } 213 | 214 | void testTake() { 215 | def integers = 0..9 216 | 217 | // fail: param cannot be <= 0 218 | shouldFail(IllegalArgumentException.class, { 219 | stream(integers).take(-1).toList() 220 | }) 221 | 222 | shouldFail(NoSuchElementException.class, { 223 | def iterator = stream(integers).take(1).iterator() 224 | iterator.next() 225 | iterator.next() 226 | }) 227 | 228 | // takes 5 items 229 | assert stream(integers).take(5).toList().size() == 5 230 | // takes 10 items (as many as are into the collection) 231 | assert stream(integers).take(10).toList().size() == 10 232 | // takes 100 items (get all items) 233 | assert stream(integers).take(100).toList().size() == 10 234 | // takes 5 and count 235 | assert stream(integers).take(5).count() == 5 236 | // takes 5 and reverses 237 | assert stream(integers).take(5).reverse().toList() == [4, 3, 2, 1, 0] 238 | } 239 | 240 | void testSkip() { 241 | def integers = 0..9 as int[] 242 | 243 | // fail: param cannot be <= 0 244 | shouldFail(IllegalArgumentException.class, { 245 | stream(integers).skip(-1).toList() 246 | }) 247 | 248 | shouldFail(NoSuchElementException.class, { 249 | stream(integers).skip(10).iterator().next() 250 | }) 251 | 252 | // skips 5 items 253 | assert stream(integers).skip(5).toList().size() == 5 254 | // skips 10 items (as many as are into the collection) 255 | assert stream(integers).skip(10).toList().size() == 0 256 | // skips 100 items (more than present in the collection) 257 | assert stream(integers).skip(100).toList().size() == 0 258 | // skips 5 and count 259 | assert stream(integers).skip(5).count() == 5 260 | // skips 5 and reverses 261 | assert stream(integers).skip(5).reverse().toList().toArray() as int[] == [9, 8, 7, 6, 5] as int [] 262 | } 263 | 264 | void testDistinct() { 265 | def integers = 0..9 266 | 267 | // no dup items 268 | assert integers == 269 | stream(integers).distinct().toList() 270 | 271 | // every item has a dup 272 | def range = new ArrayList() 273 | range.addAll(integers) 274 | range.addAll(integers) 275 | 276 | assert integers == 277 | stream(range).distinct().toList() 278 | } 279 | 280 | void testSum() { 281 | def list = 0..9 as List 282 | def sum = list.sum() 283 | 284 | // sum Bytes 285 | assert sum == stream(list).sum({ n -> (byte) n } as SelectorByte) 286 | 287 | // sum Shorts 288 | assert sum == stream(list).sum({ n -> (short) n } as SelectorShort) 289 | 290 | // sum Integers 291 | assert sum == stream(list).sum({ n -> (int) n } as SelectorInteger) 292 | 293 | // sum Longs 294 | assert sum == stream(list).sum({ n -> (long) n } as SelectorLong) 295 | 296 | // sum Floats 297 | assert sum == stream(list).sum({ n -> (float) n } as SelectorFloat) 298 | 299 | // sum Doubles 300 | assert sum == stream(list).sum({ n -> (double) n } as SelectorDouble) 301 | 302 | // sum BigDecimals 303 | assert sum == stream(list).sum({ int n -> new BigDecimal(n) } as SelectorBigDecimal) 304 | } 305 | 306 | void testAverage() { 307 | def list = 0..9 as List 308 | 309 | // average Bytes 310 | assert 4 == stream(list).average({ n -> (byte) n } as SelectorByte) 311 | 312 | // average Shorts 313 | assert 4 == stream(list).average({ n -> (short) n } as SelectorShort) 314 | 315 | // average Integers 316 | assert 4 == stream(list).average({ n -> (int) n } as SelectorInteger) 317 | 318 | // average Longs 319 | assert 4 == stream(list).average({ n -> (long) n } as SelectorLong) 320 | 321 | // average Floats 322 | assert 4.5 == stream(list).average({ n -> (float) n } as SelectorFloat) 323 | 324 | // average Doubles 325 | assert 4.5 == stream(list).average({ n -> (double) n } as SelectorDouble) 326 | 327 | // average BigDecimals 328 | assert new BigDecimal(4.5) == stream(list).average({ int n -> new BigDecimal(n) } as SelectorBigDecimal) 329 | 330 | // average BigDecimals with MathContext 331 | assert new BigDecimal(4.5) == stream(list).average({ int n -> new BigDecimal(n) } as SelectorBigDecimal, new MathContext(2, RoundingMode.HALF_UP)) 332 | 333 | // average BigDecimals with MathContext for recurring division 334 | assert new BigDecimal(0.33, new MathContext(2, RoundingMode.UP)) == stream([0, 0, 1]).average({ int n -> new BigDecimal(n) } as SelectorBigDecimal, new MathContext(2, RoundingMode.UP)) 335 | 336 | } 337 | 338 | void testMax() { 339 | 340 | // max for numbers 341 | def numbers = [3, 6, 8, 4, 2, 0, 1, 9, 7, 5] 342 | assert stream(numbers).max({ n -> n }) == 9 343 | 344 | // max for dates 345 | def dates = [ 346 | new Date(2017, 10, 20), 347 | new Date(2018, 1, 1), 348 | new Date(2012, 9, 7), 349 | new Date(2015, 7, 27) 350 | ] 351 | assert stream(dates).max({ n -> n }) == new Date(2018, 1, 1) 352 | 353 | // max for strings 354 | def strings = ["Shift", "Alê", "Falavinha", "Tomoiti", "Milton", "Michele", "Fillipe", "Zé"] 355 | assert stream(strings).max({ n -> n }) == "Zé" 356 | 357 | // max with custom comparator 358 | def objects = [ [id: 322], [id: 420], [id: 3154], [id: 123] ] 359 | assert stream(objects).max({ n -> n.id }, { n1, n2 -> n1 - n2 }).id == 3154 360 | } 361 | 362 | void testMin() { 363 | 364 | // max for numbers 365 | def numbers = [3, 6, 8, 4, 2, 0, 1, 9, 7, 5] 366 | assert stream(numbers).min({ n -> n }) == 0 367 | 368 | // max for dates 369 | def dates = [ 370 | new Date(2017, 10, 20), 371 | new Date(2018, 1, 1), 372 | new Date(2012, 9, 7), 373 | new Date(2015, 7, 27) 374 | ] 375 | assert stream(dates).min({ n -> n }) == new Date(2012, 9, 7) 376 | 377 | // max for strings 378 | def strings = ["Shift", "Alê", "Falavinha", "Tomoiti", "Milton", "Michele", "Fillipe", "Zé"] 379 | assert stream(strings).min({ n -> n }) == "Alê" 380 | 381 | // max with custom comparator 382 | def objects = [ [id: 322], [id: 420], [id: 3154], [id: 123] ] 383 | assert stream(objects).min({ n -> n.id }, { n1, n2 -> n1 - n2 }).id == 123 384 | } 385 | 386 | @SuppressWarnings("GroovyPointlessBoolean") 387 | void testAny() { 388 | def integers = 0..9 389 | 390 | // any with no parameters 391 | assert stream([]).any() == false 392 | assert stream([1]).any() == true 393 | assert stream(integers).any() == true 394 | 395 | // any passing Predicate 396 | assert stream([]).any({ n -> n == 0 } as Predicate) == false 397 | assert stream([1]).any({ n -> n == 1 } as Predicate) == true 398 | assert stream(integers).any({ n -> n == 5 } as Predicate) == true 399 | assert stream(integers).any({ n -> n == 100 } as Predicate) == false 400 | } 401 | 402 | @SuppressWarnings("GroovyPointlessBoolean") 403 | void testContains() { 404 | def integers = 0..9 405 | 406 | // contains passing element 407 | assert stream(new int[0]).contains(1) == false 408 | assert stream(integers).contains(99) == false 409 | assert stream(integers).contains(7) == true 410 | 411 | // contains passing element and compare function 412 | assert stream([]).contains(9, { n1, n2 -> n1 == n2 }) == false 413 | assert stream([1]).contains(1, { n1, n2 -> n1 == n2 }) == true 414 | assert stream(integers).contains(9, { n1, n2 -> n1 == n2 }) == true 415 | assert stream(integers).contains(100, { n1, n2 -> n1 == n2 }) == false 416 | } 417 | 418 | void testCount() { 419 | assert stream([]).count() == 0 420 | assert stream(0..9).count() == 10 421 | } 422 | 423 | void testFirst() { 424 | def integers = 0..9 425 | 426 | // first with no parameters 427 | assert stream(integers).first() == 0 428 | shouldFail(NoSuchElementException.class, { 429 | assert stream([]).first() 430 | }) 431 | 432 | // first passing Predicate 433 | assert stream(integers).first({ n -> n > 5 }) == 6 434 | shouldFail(NoSuchElementException.class, { 435 | assert stream(integers).first({ n -> n > 10 }) 436 | }) 437 | 438 | // firstOrDefault 439 | assert stream([]).firstOrDefault(10) == 10 440 | assert stream(integers).firstOrDefault(10) == 0 441 | 442 | // firstOrDefault passing Predicate 443 | assert stream(integers).firstOrDefault({ n -> n > 5 }, 10) == 6 444 | assert stream(integers).firstOrDefault({ n -> n == 100 }, -1) == -1 445 | 446 | // firstOrNull 447 | assert stream([]).firstOrNull() == null 448 | assert stream(integers).firstOrNull() == 0 449 | 450 | // firstOrDefault passing Predicate 451 | assert stream(integers).firstOrNull({ n -> n > 5 }) == 6 452 | assert stream(integers).firstOrNull({ n -> n == 100 }) == null 453 | } 454 | 455 | void testLast() { 456 | def integers = 0..9 457 | 458 | // last with no parameters 459 | assert stream(integers).last() == 9 460 | shouldFail(NoSuchElementException.class, { 461 | assert stream([]).last() 462 | }) 463 | 464 | // last passing Predicate 465 | assert stream(integers).last({ n -> n < 5 }) == 4 466 | shouldFail(NoSuchElementException.class, { 467 | assert stream(integers).last({ n -> n > 10 }) 468 | }) 469 | 470 | // lastOrDefault 471 | assert stream([]).lastOrDefault(10) == 10 472 | assert stream(integers).lastOrDefault(10) == 9 473 | 474 | // lastOrDefault passing Predicate 475 | assert stream(integers).lastOrDefault({ n -> n < 5 }, 10) == 4 476 | assert stream(integers).lastOrDefault({ n -> n == 100 }, -1) == -1 477 | 478 | // lastOrNull 479 | assert stream([]).lastOrNull() == null 480 | assert stream(integers).lastOrNull() == 9 481 | 482 | // lastOrDefault passing Predicate 483 | assert stream(integers).lastOrNull({ n -> n < 5 }) == 4 484 | assert stream(integers).lastOrNull({ n -> n == 100 }) == null 485 | } 486 | 487 | void testSingle() { 488 | def integers = 0..9 489 | 490 | // single with no parameters 491 | assert stream([5]).single() == 5 492 | shouldFail(NoSuchElementException.class, { 493 | stream([]).single() 494 | }) 495 | shouldFail(MultipleElementsFoundException.class, { 496 | stream(integers).single() 497 | }) 498 | 499 | // single passing Predicate 500 | assert stream(integers).single({ n -> n == 5 }) == 5 501 | shouldFail(NoSuchElementException.class, { 502 | stream(integers).single({ n -> n == 10 }) 503 | }) 504 | shouldFail(MultipleElementsFoundException.class, { 505 | stream(integers).single({ n -> n > 5 }) 506 | }) 507 | 508 | // singleOrDefault with no parameters 509 | assert stream([5]).singleOrDefault(10) == 5 510 | assert stream([]).singleOrDefault(10) == 10 511 | shouldFail(MultipleElementsFoundException.class, { 512 | stream(integers).singleOrDefault(10) 513 | }) 514 | 515 | // singleOrDefault passing Predicate 516 | assert stream(integers).singleOrDefault({ n -> n == 5 }, -1) == 5 517 | assert stream(integers).singleOrDefault({ n -> n == 10 }, -1) == -1 518 | shouldFail(MultipleElementsFoundException.class, { 519 | stream(integers).singleOrDefault({ n -> n > 5 }, -1) 520 | }) 521 | 522 | // singleOrNull with no parameters 523 | assert stream([5]).singleOrNull() == 5 524 | assert stream([]).singleOrNull() == null 525 | shouldFail(MultipleElementsFoundException.class, { 526 | stream(integers).singleOrNull() 527 | }) 528 | 529 | // singleOrNull passing Predicate 530 | assert stream(integers).singleOrNull({ n -> n == 5 }) == 5 531 | assert stream(integers).singleOrNull({ n -> n == 10 }) == null 532 | shouldFail(MultipleElementsFoundException.class, { 533 | stream(integers).singleOrNull({ n -> n > 5 }) 534 | }) 535 | } 536 | 537 | void testToList() { 538 | def range = 0..9 539 | def result = stream(range).toList() 540 | assert result.size() == 10 541 | assert result instanceof List 542 | assert result == range 543 | } 544 | 545 | void testToMap() { 546 | def map 547 | def integers = 0..9 548 | def keySelector = { n -> n } as Selector 549 | def elementSelector = { int n -> n * 10 } as Selector 550 | 551 | // map with key selector only 552 | map = stream(integers).toMap(keySelector) 553 | 554 | assert map.keySet().size() == integers.size() 555 | assert map.values().toList() == (integers) 556 | 557 | // map with key and element selectors 558 | map = stream(integers).toMap(keySelector, elementSelector) 559 | 560 | assert map.keySet().size() == integers.size() 561 | assert map.values().toList() == integers.collect { it * 10 } 562 | 563 | // empty Iterable generate empty maps 564 | assert stream([]).toMap(keySelector).isEmpty() 565 | assert stream([]).toMap(keySelector, elementSelector).isEmpty() 566 | } 567 | 568 | void testReverse() { 569 | def integers = 0..9 570 | 571 | assert integers == 572 | stream(integers).reverse().reverse().toList() 573 | } 574 | } 575 | 576 | -------------------------------------------------------------------------------- /src/main/java/br/com/zbra/androidlinq/Stream.java: -------------------------------------------------------------------------------- 1 | package br.com.zbra.androidlinq; 2 | 3 | import br.com.zbra.androidlinq.delegate.*; 4 | import br.com.zbra.androidlinq.exception.MultipleElementsFoundException; 5 | 6 | import java.math.BigDecimal; 7 | import java.math.MathContext; 8 | import java.util.List; 9 | import java.util.Map; 10 | import java.util.NoSuchElementException; 11 | 12 | /** 13 | * Decorates {@code Iterable} objects to enable use of Linq like expressions. 14 | * 15 | * @param the type of the wrapped {@code Iterable} 16 | */ 17 | public interface Stream extends Iterable { 18 | /** 19 | * Filters a sequence of values based on a predicate. 20 | * 21 | * @param predicate A function to test each element for a condition. 22 | * @return An Stream of type T that contains elements from the input sequence that satisfy the condition. 23 | */ 24 | Stream where(Predicate predicate); 25 | 26 | /** 27 | * Projects each element of a sequence into a new form. 28 | * 29 | * @param selector A transform function to apply to each element. 30 | * @param The type of the value returned by selector. 31 | * @return An Stream of type TResult whose elements are the result of invoking the transform function on each element of source. 32 | */ 33 | Stream select(Selector selector); 34 | 35 | /** 36 | * Projects each element of a sequence to an {@link Iterable Iterable<TResult>} and flattens the resulting sequences into one sequence. 37 | * 38 | * @param selector A transform function to apply to each element. 39 | * @param The type of the elements of the sequence returned by selector. 40 | * @return An Stream of type TResult whose elements are the result of invoking the one-to-many transform function on each element of the input sequence. 41 | */ 42 | Stream selectMany(Selector> selector); 43 | 44 | /** 45 | * Groups the elements of a sequence according to a specified key selector function. 46 | * 47 | * @param keySelector A function to extract the key for each source element. 48 | * @param elementSelector A function to map each source element to an element into a Grouping. 49 | * @param The type of the key returned by keySelector. 50 | * @param The type of the element returned by elementSelector. 51 | * @return An Stream of type Grouping where each Grouping object contains a sequence of objects and a key (TKey, TResult respectively). 52 | */ 53 | Stream> groupBy(Selector keySelector, Selector elementSelector); 54 | 55 | /** 56 | * Groups the elements of a sequence according to a specified key selector function. 57 | * 58 | * @param keySelector A function to extract the key for each source element. 59 | * @param The type of the key returned by keySelector. 60 | * @return An Stream of type Grouping where each Grouping object contains a sequence of objects and a key (TKey, TResult respectively). 61 | */ 62 | Stream> groupBy(Selector keySelector); 63 | 64 | /** 65 | * Sorts the elements of a sequence in ascending order according to a key. 66 | * 67 | * @param keySelector A function to extract a key from an element. 68 | * @param comparator An Comparator to compare keys. 69 | * @param The type of the key returned by keySelector. 70 | * @return An Stream of type T whose elements are sorted according to a key. 71 | */ 72 | OrderedStream orderBy(Selector keySelector, Comparator comparator); 73 | 74 | /** 75 | * Sorts the elements of a sequence in ascending order according to a key. 76 | * 77 | * @param keySelector A function to extract a key from an element. 78 | * @param The type of the key returned by keySelector. 79 | * @return An Stream of type T whose elements are sorted according to a key. 80 | */ 81 | > OrderedStream orderBy(Selector keySelector); 82 | 83 | /** 84 | * Sorts the elements of a sequence in descending order according to a key. 85 | * 86 | * @param keySelector A function to extract a key from an element. 87 | * @param comparator An Comparator to compare keys. 88 | * @param The type of the key returned by keySelector. 89 | * @return An Stream of type T whose elements are sorted according to a key. 90 | */ 91 | OrderedStream orderByDescending(Selector keySelector, Comparator comparator); 92 | 93 | /** 94 | * Sorts the elements of a sequence in descending order according to a key. 95 | * 96 | * @param keySelector A function to extract a key from an element. 97 | * @param The type of the key returned by keySelector. 98 | * @return An Stream of type T whose elements are sorted according to a key. 99 | */ 100 | > OrderedStream orderByDescending(Selector keySelector); 101 | 102 | /** 103 | * Reverses the order of the sequence. 104 | * 105 | * @return An stream of type T whose elements are in the reverse order. 106 | */ 107 | Stream reverse(); 108 | 109 | /** 110 | * Applies an accumulator function over a sequence. 111 | * The specified seed value is used as the initial accumulator value, 112 | * and the specified function is used to select the result value. 113 | * 114 | * @param seed The initial accumulator value. 115 | * @param aggregator An accumulator function to be invoked on each element. 116 | * @param The type of the accumulator value. 117 | * @return The transformed final accumulator value. 118 | */ 119 | TAccumulate aggregate(TAccumulate seed, Aggregator aggregator); 120 | 121 | /** 122 | * Returns a specified number of contiguous elements from the start of a sequence. 123 | * 124 | * @param count The number of elements to return. 125 | * @return An Stream of type T that contains the specified number of elements from the start of the input sequence. 126 | */ 127 | Stream take(int count); 128 | 129 | /** 130 | * Skips a specified number of contiguous elements from the start of a sequence then returns remaining elements. 131 | * 132 | * @param count The number of elements to skip. 133 | * @return An Stream of type T that contains the elements after the number of elements skipped from the start of the input sequence. 134 | */ 135 | Stream skip(int count); 136 | 137 | /** 138 | * Returns distinct elements from a sequence by using the object {@code equals()} to compare values. 139 | * 140 | * @return An Stream of type T that contains distinct elements from the source sequence. 141 | */ 142 | Stream distinct(); 143 | 144 | /** 145 | * Computes the sum of the sequence of Byte values that are obtained by invoking 146 | * a transform function on each element of the input sequence. 147 | * 148 | * @param selector A transform function to apply to each element. 149 | * @return The sum of the projected values. 150 | */ 151 | Byte sum(SelectorByte selector); 152 | 153 | /** 154 | * Computes the sum of the sequence of Short values that are obtained by invoking 155 | * a transform function on each element of the input sequence. 156 | * 157 | * @param selector A transform function to apply to each element. 158 | * @return The sum of the projected values. 159 | */ 160 | Short sum(SelectorShort selector); 161 | 162 | /** 163 | * Computes the sum of the sequence of Integer values that are obtained by invoking 164 | * a transform function on each element of the input sequence. 165 | * 166 | * @param selector A transform function to apply to each element. 167 | * @return The sum of the projected values. 168 | */ 169 | Integer sum(SelectorInteger selector); 170 | 171 | /** 172 | * Computes the sum of the sequence of Long values that are obtained by invoking 173 | * a transform function on each element of the input sequence. 174 | * 175 | * @param selector A transform function to apply to each element. 176 | * @return The sum of the projected values. 177 | */ 178 | Long sum(SelectorLong selector); 179 | 180 | /** 181 | * Computes the sum of the sequence of Float values that are obtained by invoking 182 | * a transform function on each element of the input sequence. 183 | * 184 | * @param selector A transform function to apply to each element. 185 | * @return The sum of the projected values. 186 | */ 187 | Float sum(SelectorFloat selector); 188 | 189 | /** 190 | * Computes the sum of the sequence of Double values that are obtained by invoking 191 | * a transform function on each element of the input sequence. 192 | * 193 | * @param selector A transform function to apply to each element. 194 | * @return The sum of the projected values. 195 | */ 196 | Double sum(SelectorDouble selector); 197 | 198 | /** 199 | * Computes the sum of the sequence of BigDecimal values that are obtained by invoking 200 | * a transform function on each element of the input sequence. 201 | * 202 | * @param selector A transform function to apply to each element. 203 | * @return The sum of the projected values. 204 | */ 205 | BigDecimal sum(SelectorBigDecimal selector); 206 | 207 | /** 208 | * Computes the average of the sequence of Byte values that are obtained by invoking 209 | * a transform function on each element of the input sequence. 210 | * 211 | * @param selector A transform function to apply to each element. 212 | * @return The average of the projected values. 213 | */ 214 | Byte average(SelectorByte selector); 215 | 216 | /** 217 | * Computes the average of the sequence of Short values that are obtained by invoking 218 | * a transform function on each element of the input sequence. 219 | * 220 | * @param selector A transform function to apply to each element. 221 | * @return The average of the projected values. 222 | */ 223 | Short average(SelectorShort selector); 224 | 225 | /** 226 | * Computes the average of the sequence of Integer values that are obtained by invoking 227 | * a transform function on each element of the input sequence. 228 | * 229 | * @param selector A transform function to apply to each element. 230 | * @return The average of the projected values. 231 | */ 232 | Integer average(SelectorInteger selector); 233 | 234 | /** 235 | * Computes the average of the sequence of Long values that are obtained by invoking 236 | * a transform function on each element of the input sequence. 237 | * 238 | * @param selector A transform function to apply to each element. 239 | * @return The average of the projected values. 240 | */ 241 | Long average(SelectorLong selector); 242 | 243 | /** 244 | * Computes the average of the sequence of Float values that are obtained by invoking 245 | * a transform function on each element of the input sequence. 246 | * 247 | * @param selector A transform function to apply to each element. 248 | * @return The average of the projected values. 249 | */ 250 | Float average(SelectorFloat selector); 251 | 252 | /** 253 | * Computes the average of the sequence of Double values that are obtained by invoking 254 | * a transform function on each element of the input sequence. 255 | * 256 | * @param selector A transform function to apply to each element. 257 | * @return The average of the projected values. 258 | */ 259 | Double average(SelectorDouble selector); 260 | 261 | /** 262 | * Computes the average of the sequence of BigDecimal values that are obtained by invoking 263 | * a transform function on each element of the input sequence. 264 | * 265 | * @param selector A transform function to apply to each element. 266 | * @return The average of the projected values. 267 | */ 268 | BigDecimal average(SelectorBigDecimal selector); 269 | 270 | /** 271 | * Computes the average of the sequence of BigDecimal values that are obtained by invoking 272 | * a transform function on each element of the input sequence. 273 | * 274 | * @param selector A transform function to apply to each element. 275 | * @param mathContext Math context for operating with theses BigDecimals 276 | * @return The average of the projected values. 277 | * @see BigDecimal#divide(BigDecimal, MathContext) 278 | * @see MathContext 279 | */ 280 | BigDecimal average(SelectorBigDecimal selector, MathContext mathContext); 281 | 282 | /** 283 | * Computes the min of the sequence of values that are obtained by invoking 284 | * a transform function on each element of the input sequence. 285 | * 286 | * @param selector A transform function to apply to each element. 287 | * @param The resulting type returned by the selector. 288 | * @return The max of the projected values. 289 | */ 290 | > T min(Selector selector); 291 | 292 | /** 293 | * Computes the min of the sequence of values that are obtained by invoking 294 | * a transform function on each element of the input sequence. 295 | * 296 | * @param selector A transform function to apply to each element. 297 | * @param comparator A comparator function that defines element ordering. 298 | * @param The resulting type returned by the selector. 299 | * @return The max of the projected values. 300 | */ 301 | T min(Selector selector, Comparator comparator); 302 | 303 | /** 304 | * Computes the max of the sequence of values that are obtained by invoking 305 | * a transform function on each element of the input sequence. 306 | * 307 | * @param selector A transform function to apply to each element. 308 | * @param The resulting type returned by the selector. 309 | * @return The max of the projected values. 310 | */ 311 | > T max(Selector selector); 312 | 313 | /** 314 | * Computes the min of the sequence of values that are obtained by invoking 315 | * a transform function on each element of the input sequence. 316 | * 317 | * @param selector A transform function to apply to each element. 318 | * @param comparator A comparator function that defines element ordering. 319 | * @param The resulting type returned by the selector. 320 | * @return The max of the projected values. 321 | */ 322 | T max(Selector selector, Comparator comparator); 323 | 324 | /** 325 | * Determines whether a sequence contains any elements. 326 | * 327 | * @return {@code true} if the source sequence contains any elements; otherwise, {@code false}. 328 | */ 329 | boolean any(); 330 | 331 | /** 332 | * Determines whether any element of a sequence satisfies a condition. 333 | * 334 | * @param predicate A function to test each element for a condition 335 | * @return {@code true} if any elements in the source sequence pass the test in the specified predicate; otherwise, {@code false}. 336 | */ 337 | boolean any(Predicate predicate); 338 | 339 | /** 340 | * Determines whether a sequence contains a specific element. 341 | * 342 | * @param element element for checking 343 | * @return {@code true} if the source sequence contains the given element; otherwise, {@code false}. 344 | */ 345 | boolean contains(T element); 346 | 347 | /** 348 | * Determines whether a sequence contains a specific element. 349 | * 350 | * @param element element for checking 351 | * @param comparator expression defining how to compare elements 352 | * @return {@code true} if the source sequence contains the given element; otherwise, {@code false}. 353 | */ 354 | boolean contains(T element, EqualityComparator comparator); 355 | 356 | /** 357 | * Returns the number of elements in a sequence. 358 | * 359 | * @return The number of elements in the input sequence. 360 | */ 361 | int count(); 362 | 363 | /** 364 | * Returns the first element of a sequence; this method throws an exception if the sequence is empty. 365 | * 366 | * @return The first element in the specified sequence. 367 | * @throws NoSuchElementException if the sequence is empty. 368 | */ 369 | T first(); 370 | 371 | /** 372 | * Returns the first element in a sequence that satisfies the specified condition; this method throws 373 | * an exception if no element in the sequence satisfies the condition. 374 | * 375 | * @param predicate A function to test each element for a condition. 376 | * @return The first element in the sequence that passes the test in the specified predicate function. 377 | * @throws NoSuchElementException if no element in the sequence satisfies the condition. 378 | */ 379 | T first(Predicate predicate); 380 | 381 | /** 382 | * Returns the first element of a sequence, or null if empty. 383 | * 384 | * @return The first element in the specified sequence, or null if empty. 385 | */ 386 | T firstOrNull(); 387 | 388 | /** 389 | * Returns the first element in a sequence that satisfies a specified condition, or null if none does. 390 | * 391 | * @param predicate A function to test each element for a condition. 392 | * @return The first element in the sequence that passes the test in the specified predicate function, or null if none does. 393 | */ 394 | T firstOrNull(Predicate predicate); 395 | 396 | /** 397 | * Returns the first element of a sequence, or defaultValue if empty. 398 | * 399 | * @return The first element in the specified sequence, or defaultValue if empty. 400 | */ 401 | T firstOrDefault(T defaultValue); 402 | 403 | /** 404 | * Returns the first element in a sequence that satisfies a specified condition, or defaultValue if none does. 405 | * 406 | * @param predicate A function to test each element for a condition. 407 | * @return The first element in the sequence that passes the test in the specified predicate function, or or defaultValue if none does. 408 | */ 409 | T firstOrDefault(Predicate predicate, T defaultValue); 410 | 411 | /** 412 | * Returns the last element of a sequence; this method throws an exception if the sequence is empty. 413 | * 414 | * @return The last element in the specified sequence. 415 | * @throws NoSuchElementException if the sequence is empty. 416 | */ 417 | T last(); 418 | 419 | /** 420 | * Returns the last element in a sequence that satisfies the specified condition; this method throws 421 | * an exception if no element in the sequence satisfies the condition. 422 | * 423 | * @param predicate A function to test each element for a condition. 424 | * @return The last element in the sequence that passes the test in the specified predicate function. 425 | * @throws NoSuchElementException if no element in the sequence satisfies the condition. 426 | */ 427 | T last(Predicate predicate); 428 | 429 | /** 430 | * Returns the last element of a sequence, or null if empty. 431 | * 432 | * @return The last element in the specified sequence, or null if empty. 433 | */ 434 | T lastOrNull(); 435 | 436 | /** 437 | * Returns the last element in a sequence that satisfies a specified condition, or null if none does. 438 | * 439 | * @param predicate A function to test each element for a condition. 440 | * @return The last element in the sequence that passes the test in the specified predicate function, or null if none does. 441 | */ 442 | T lastOrNull(Predicate predicate); 443 | 444 | /** 445 | * Returns the last element of a sequence, or defaultValue if empty. 446 | * 447 | * @return The last element in the specified sequence, or defaultValue if empty. 448 | */ 449 | T lastOrDefault(T defaultValue); 450 | 451 | /** 452 | * Returns the last element in a sequence that satisfies a specified condition, or defaultValue if none does. 453 | * 454 | * @param predicate A function to test each element for a condition. 455 | * @return The last element in the sequence that passes the test in the specified predicate function, or or defaultValue if none does. 456 | */ 457 | T lastOrDefault(Predicate predicate, T defaultValue); 458 | 459 | /** 460 | * Returns the only element of a sequence; this method throws an exception if there is more than 461 | * one element in the sequence or if the sequence is empty. 462 | * 463 | * @return The single element of the input sequence, or default(TSource) if the sequence contains no elements. 464 | * @throws MultipleElementsFoundException The input sequence contains more than one matching element. 465 | * @throws NoSuchElementException If the sequence is empty. 466 | */ 467 | T single() throws MultipleElementsFoundException; 468 | 469 | /** 470 | * Returns the only element of a sequence that satisfies a specified condition; this method throws an exception 471 | * if multiple elements in the sequence satisfy the condition or if none does. 472 | * 473 | * @param predicate A function to test an element for a condition. 474 | * @return The single element of the input sequence that satisfies the condition, or null if no such element is found. 475 | * @throws MultipleElementsFoundException The input sequence contains more than one matching element. 476 | * @throws NoSuchElementException If no element in the sequence satisfies the condition. 477 | */ 478 | T single(Predicate predicate) throws MultipleElementsFoundException; 479 | 480 | /** 481 | * Returns the only element of a sequence, or a null if the sequence is empty; this method throws 482 | * an exception if there is more than one element in the sequence. 483 | * 484 | * @return The single element of the input sequence, or default(TSource) if the sequence contains no elements. 485 | * @throws MultipleElementsFoundException The input sequence contains more than one matching element. 486 | */ 487 | T singleOrNull(); 488 | 489 | /** 490 | * Returns the only element of a sequence that satisfies a specified condition or null if 491 | * no such element exists; this method throws an exception if more than one element satisfies the condition. 492 | * 493 | * @param predicate A function to test an element for a condition. 494 | * @return The single element of the input sequence that satisfies the condition, or null if no such element is found. 495 | * @throws MultipleElementsFoundException The input sequence contains more than one matching element. 496 | */ 497 | T singleOrNull(Predicate predicate); 498 | 499 | /** 500 | * Returns the only element of a sequence, or a defaultValue if the sequence is empty; this method throws 501 | * an exception if there is more than one element in the sequence. 502 | * 503 | * @return The single element of the input sequence, or default(TSource) if the sequence contains no elements. 504 | * @throws MultipleElementsFoundException The input sequence contains more than one matching element. 505 | */ 506 | T singleOrDefault(T defaultValue); 507 | 508 | /** 509 | * Returns the only element of a sequence that satisfies a specified condition or defaultValue if 510 | * no such element exists; this method throws an exception if more than one element satisfies the condition. 511 | * 512 | * @param predicate A function to test an element for a condition. 513 | * @return The single element of the input sequence that satisfies the condition, or null if no such element is found. 514 | * @throws MultipleElementsFoundException The input sequence contains more than one matching element. 515 | */ 516 | T singleOrDefault(Predicate predicate, T defaultValue); 517 | 518 | /** 519 | * Creates a List from a Stream. 520 | * 521 | * @return A List of type T that contains elements from the input sequence. 522 | */ 523 | List toList(); 524 | 525 | /** 526 | * Creates a Map<TKey, T> from a Stream<T> according to a specified key selector function. 527 | * 528 | * @param keySelector A function to extract a key from each element. 529 | * @param The type of the key returned by keySelector. 530 | * @return A Map that contains keys and values derived from the Stream. 531 | */ 532 | Map toMap(Selector keySelector); 533 | 534 | /** 535 | * Creates a Map<TKey, T> from a Stream<T> according to specified key selector and value selector functions. 536 | * 537 | * @param keySelector A function to extract a key from each element. 538 | * @param valueSelector A transform function to produce a result element value from each element. 539 | * @param The type of the key returned by keySelector. 540 | * @param The type of the value returned by elementSelector. 541 | * @return A Map that contains keys and values derived from the Stream. 542 | */ 543 | Map toMap(Selector keySelector, Selector valueSelector); 544 | } 545 | --------------------------------------------------------------------------------