manifold.collections.extensions.java.lang.Iterable.ManIterableExt Maven / Gradle / Ivy
/*
* Copyright (c) 2018 - Manifold Systems LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package manifold.collections.extensions.java.lang.Iterable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.Predicate;
import manifold.ext.rt.api.*;
import manifold.rt.api.util.Pair;
import static java.util.Collections.emptyList;
/**
*/
@Extension
public class ManIterableExt
{
/**
* Returns first element.
*
* @throws NoSuchElementException if the collection is empty.
*/
public static T first( @This Iterable thiz )
{
if( thiz instanceof List )
{
return ((List)thiz).first();
}
else
{
Iterator iterator = thiz.iterator();
if( !iterator.hasNext() )
{
throw new NoSuchElementException( "Collection is empty." );
}
return iterator.next();
}
}
/**
* Returns the first element matching the given {@code predicate}.
*
* @throws {@code NoSuchElementException} if no such element is found.
*/
public static T first( @This Iterable thiz, Predicate predicate )
{
for( T element : thiz )
{
if( predicate.test( element ) )
{
return element;
}
}
throw new NoSuchElementException( "Collection contains no element matching the predicate." );
}
/**
* Returns index of the first element matching the given {@code predicate}, or -1 if the collection does not contain such element.
*/
public static int indexOfFirst( @This Iterable thiz, Predicate predicate )
{
int index = 0;
for( T item : thiz )
{
if( predicate.test( item ) )
{
return index;
}
index++;
}
return -1;
}
/**
* Returns the first element, or null if the collection is empty.
*/
public static T firstOrNull( @This Iterable thiz )
{
if( thiz instanceof List )
{
if( ((List)thiz).isEmpty() )
{
return null;
}
else
{
return ((List)thiz).get( 0 );
}
}
else
{
Iterator iterator = thiz.iterator();
if( !iterator.hasNext() )
{
return null;
}
return iterator.next();
}
}
/**
* Returns the first element matching the given {@code predicate}, or {@code null} if element was not found.
*/
public static T firstOrNull( @This Iterable thiz, Predicate predicate )
{
for( T element : thiz )
{
if( predicate.test( element ) )
{
return element;
}
}
return null;
}
/**
* Returns the last element.
*
* @throws NoSuchElementException if the collection is empty.
*/
public static T last( @This Iterable thiz )
{
if( thiz instanceof List )
{
return ((List)thiz).last();
}
else
{
Iterator iterator = thiz.iterator();
if( !iterator.hasNext() )
{
throw new NoSuchElementException( "Collection is empty." );
}
T last = iterator.next();
while( iterator.hasNext() )
{
last = iterator.next();
}
return last;
}
}
/**
* Returns the last element matching the given {@code predicate}.
*
* @throws NoSuchElementException if no such element is found.
*/
public static T last( @This Iterable thiz, Predicate predicate )
{
T last = null;
boolean found = false;
for( T element : thiz )
{
if( predicate.test( element ) )
{
last = element;
found = true;
}
}
if( !found )
{
throw new NoSuchElementException( "Collection contains no element matching the predicate." );
}
return last;
}
/**
* Returns index of the last element matching the given {@code predicate}, or -1 if the collection does not contain such element.
*/
public static int indexOfLast( @This Iterable thiz, Predicate predicate )
{
int lastIndex = -1;
int index = 0;
for( T item : thiz )
{
if( predicate.test( item ) )
{
lastIndex = index;
}
index++;
}
return lastIndex;
}
/**
* Returns the last element, or {@code null} if the collection is empty.
*/
public static T lastOrNull( @This Iterable thiz )
{
if( thiz instanceof List )
{
return ((List)thiz).isEmpty() ? null : ((List)thiz).get( ((List)thiz).size() - 1 );
}
else
{
Iterator iterator = thiz.iterator();
if( !iterator.hasNext() )
{
return null;
}
T last = iterator.next();
while( iterator.hasNext() )
{
last = iterator.next();
}
return last;
}
}
/**
* Returns the last element matching the given {@code predicate}, or {@code null} if no such element was found.
*/
public static T lastOrNull( @This Iterable thiz, Predicate predicate )
{
T last = null;
for( T element : thiz )
{
if( predicate.test( element ) )
{
last = element;
}
}
return last;
}
/**
* Returns the single element, or throws an exception if the collection is empty or has more than one element.
*/
public static T single( @This Iterable thiz )
{
if( thiz instanceof List )
{
return ((List)thiz).single();
}
else
{
Iterator iterator = thiz.iterator();
if( !iterator.hasNext() )
{
throw new NoSuchElementException( "Collection is empty." );
}
T single = iterator.next();
if( iterator.hasNext() )
{
throw new IllegalArgumentException( "Collection has more than one element." );
}
return single;
}
}
/**
* Returns the single element matching the given {@code predicate}, or throws exception if there is no or more than one matching element.
*/
public static T single( @This Iterable thiz, Predicate predicate )
{
T single = null;
boolean found = false;
for( T element : thiz )
{
if( predicate.test( element ) )
{
if( found )
{
throw new IllegalArgumentException( "Collection contains more than one matching element." );
}
single = element;
found = true;
}
}
if( !found )
{
throw new NoSuchElementException( "Collection contains no element matching the predicate." );
}
return single;
}
/**
* Returns single element, or {@code null} if the collection is empty or has more than one element.
*/
public static T singleOrNull( @This Iterable thiz )
{
if( thiz instanceof List )
{
return ((List)thiz).size() == 1 ? ((List)thiz).get( 0 ) : null;
}
else
{
Iterator iterator = thiz.iterator();
if( !iterator.hasNext() )
{
return null;
}
T single = iterator.next();
if( iterator.hasNext() )
{
return null;
}
return single;
}
}
/**
* Returns the single element matching the given {@code predicate}, or {@code null} if element was not found or more than one element was found.
*/
public static T singleOrNull( @This Iterable thiz, Predicate predicate )
{
T single = null;
boolean found = false;
for( T element : thiz )
{
if( predicate.test( element ) )
{
if( found )
{
return null;
}
single = element;
found = true;
}
}
if( !found )
{
return null;
}
return single;
}
/**
* Returns a list containing all elements matching the given {@code predicate}.
*/
public static List filterToList( @This Iterable thiz, Predicate predicate )
{
return thiz.filterTo( new ArrayList<>(), predicate );
}
/**
* Appends all elements matching the given {@code predicate} to the given {@code destination}.
*/
public static > C filterTo( @This Iterable thiz, C destination, Predicate predicate )
{
for( T element : thiz )
{
if( predicate.test( element ) )
{
destination.add( element );
}
}
return destination;
}
/**
* Returns a list containing only elements matching the given {@code predicate}.
*
* @param predicate function that takes the index of an element and the element itself
* and returns the result of predicate evaluation on the element.
*/
public static List filterIndexedToList( @This Iterable thiz, IndexedPredicate predicate )
{
return thiz.filterIndexedTo( new ArrayList(), predicate );
}
/**
* Appends all elements matching the given {@code predicate} to the given {@code destination}.
*
* @param predicate function that takes the index of an element and the element itself
* and returns the result of predicate evaluation on the element.
*/
public static > C filterIndexedTo( @This Iterable thiz, C destination, IndexedPredicate predicate )
{
thiz.forEachIndexed( ( index, element ) ->
{
if( predicate.test( index, element ) )
{
destination.add( element );
}
} );
return destination;
}
/**
* Returns a list containing all elements not matching the given {@code predicate}.
*/
public static List filterNotToList( @This Iterable thiz, Predicate predicate )
{
return thiz.filterNotTo( new ArrayList(), predicate );
}
/**
* Appends all elements not matching the given {@code predicate} to the given {@code destination}.
*/
public static > C filterNotTo( @This Iterable thiz, C destination, Predicate predicate )
{
for( T element : thiz )
{
if( !predicate.test( element ) )
{
destination.add( element );
}
}
return destination;
}
/**
* Returns a collection with elements in reversed order.
*
* Note, this method expires with JDK 21 because it interferes with new methods: {@code SequencedCollection#reversed()}
* and {@code List#reversed()}.These new methods provide the same functionality, but only for SequencedCollection.
*/
@Expires(21)
public static Collection reversed( @This Iterable thiz )
{
List list = thiz.toList();
if( list.size() <= 1 )
{
return list;
}
list.reverse();
return list;
}
/**
* Returns a {@code List} containing all elements.
*/
public static List toList( @This Iterable thiz )
{
if( thiz instanceof Collection )
{
return ((Collection)thiz).toList();
}
ArrayList list = new ArrayList<>();
for( T elem : thiz )
{
list.add( elem );
}
return list;
}
/**
* Returns a {@code Set} containing all unique elements.
*
* The returned set preserves the element iteration order of the original collection.
*/
public static Set toSet( @This Iterable thiz )
{
if( thiz instanceof Collection )
{
return ((Collection)thiz).toSet();
}
LinkedHashSet set = new LinkedHashSet<>();
for( T elem : thiz )
{
set.add( elem );
}
return set;
}
/**
* Returns a single list of all elements yielded from results of {@code transform} function being invoked on each element of original collection.
*/
public static List flatMap( @This Iterable thiz, Function> transform )
{
return thiz.flatMapTo( new ArrayList(), transform );
}
/**
* Appends all elements yielded from results of {@code transform} function being invoked on each element of original collection, to the given {@code destination}.
*/
public static > C flatMapTo( @This Iterable thiz, C destination, Function> transform )
{
for( T element : thiz )
{
Iterable list = transform.apply( element );
destination.addAll( list );
}
return destination;
}
/**
* Returns a list containing only distinct elements from the given collection.
*
* The elements in the resulting list are in the same order as they were in the source collection.
*/
public static List distinctList( @This Iterable thiz )
{
return thiz.toSet().toList();
}
/**
* Returns a list containing only elements from the given collection
* having distinct keys returned by the given {@code selector} function.
*
* The elements in the resulting list are in the same order as they were in the source collection.
*/
public static List distinctBy( @This Iterable thiz, Function selector )
{
HashSet set = new HashSet<>();
ArrayList list = new ArrayList<>();
for( T e : thiz )
{
K key = selector.apply( e );
if( set.add( key ) )
{
list.add( e );
}
}
return list;
}
/**
* Returns a set containing all elements that are contained by both thiz set and the specified collection.
*
* The returned set preserves the element iteration order of the original collection.
*/
public static Set intersect( @This Iterable thiz, Iterable other )
{
Set set = thiz.toSet();
set.retainAll( coerceToUniqueCollection( other ) );
return set;
}
/**
* Returns a set containing all elements that are contained by thiz collection and not contained by the specified collection.
*
* The returned set preserves the element iteration order of the original collection.
*/
public static Set subtract( @This Iterable thiz, Iterable other )
{
Set set = thiz.toSet();
set.removeAll( coerceToUniqueCollection( other ) );
return set;
}
/**
* Returns a set containing all distinct elements from both collections.
*
* The returned set preserves the element iteration order of the original collection.
* Those elements of the {@code other} collection that are unique are iterated in the end
* in the order of the {@code other} collection.
*/
public static Set union( @This Iterable thiz, Iterable other )
{
Set set = thiz.toSet();
set.addAll( other );
return set;
}
private static Collection coerceToUniqueCollection( Iterable source )
{
if( source instanceof Collection && ((Collection)source).size() <= 1 )
{
return (Collection)source;
}
HashSet set = new HashSet<>();
for( T elem : source )
{
set.add( elem );
}
return set;
}
/**
* Returns the number of elements in thiz collection.
*/
public static int count( @This Iterable thiz )
{
if( thiz instanceof Collection )
{
return ((Collection)thiz).size();
}
int count = 0;
for( T element : thiz )
{
count++;
}
return count;
}
/**
* Returns the number of elements matching the given {@code predicate}.
*/
public static int count( @This Iterable thiz, Predicate predicate )
{
int count = 0;
for( T element : thiz )
{
if( predicate.test( element ) )
{
count++;
}
}
return count;
}
/**
* Returns the first element having the largest value according to the provided {@code comparator} or {@code null} if there are no elements.
*/
public static T maxWith( @This Iterable thiz, Comparator comparator )
{
Iterator iterator = thiz.iterator();
if( !iterator.hasNext() )
{
return null;
}
T max = iterator.next();
while( iterator.hasNext() )
{
T e = iterator.next();
if( comparator.compare( max, e ) < 0 )
{
max = e;
}
}
return max;
}
/**
* Returns the first element having the smallest value according to the provided {@code comparator} or {@code null} if there are no elements.
*/
public static T minWith( @This Iterable thiz, Comparator comparator )
{
Iterator iterator = thiz.iterator();
if( !iterator.hasNext() )
{
return null;
}
T min = iterator.next();
while( iterator.hasNext() )
{
T e = iterator.next();
if( comparator.compare( min, e ) > 0 )
{
min = e;
}
}
return min;
}
/**
* Splits the original collection into pair of lists,
* where first list contains elements for which {@code predicate} yielded {@code true},
* while second list contains elements for which {@code predicate} yielded {@code false}.
*/
public static Pair, List> partition( @This Iterable thiz, Predicate predicate )
{
ArrayList first = new ArrayList();
ArrayList second = new ArrayList();
for( T element : thiz )
{
if( predicate.test( element ) )
{
first.add( element );
}
else
{
second.add( element );
}
}
return new Pair<>( first, second );
}
private static int collectionSizeOrDefault( Iterable thiz, int def )
{
return thiz instanceof Collection ? ((Collection)thiz).size() : def;
}
/**
* Returns a list containing the results of applying the given {@code transform} function
* to each element in the original collection.
*/
public static List mapToList( @This Iterable thiz, Function transform )
{
return thiz.mapTo( new ArrayList( collectionSizeOrDefault( thiz, 10 ) ), transform );
}
/**
* Returns a list containing the results of applying the given {@code transform} function
* to each element and its index in the original collection.
*
* @param transform function that takes the index of an element and the element itself
* and returns the result of the transform applied to the element.
* @deprecated Use {@link #mapIndexedToList(Iterable, IndexedFunction)} instead.
*/
@Deprecated
public static List mapIndexed( @This Iterable thiz, IndexedFunction transform )
{
return thiz.mapIndexedTo( new ArrayList( collectionSizeOrDefault( thiz, 10 ) ), transform );
}
/**
* Returns a list containing the results of applying the given {@code transform} function
* to each element and its index in the original collection.
*
* @param transform function that takes the index of an element and the element itself
* and returns the result of the transform applied to the element.
*/
public static List mapIndexedToList( @This Iterable thiz, IndexedFunction transform )
{
return thiz.mapIndexedTo( new ArrayList( collectionSizeOrDefault( thiz, 10 ) ), transform );
}
/**
* Returns a list containing only the non-null results of applying the given {@code transform} function
* to each element and its index in the original collection.
*
* @param transform function that takes the index of an element and the element itself
* and returns the result of the transform applied to the element.
* @deprecated Use {@link #mapIndexedNotNullToList(Iterable, IndexedFunction)} instead.
*/
@Deprecated
public static List mapIndexedNotNull( @This Iterable thiz, IndexedFunction transform )
{
return thiz.mapIndexedNotNullTo( new ArrayList(), transform );
}
/**
* Returns a list containing only the non-null results of applying the given {@code transform} function
* to each element and its index in the original collection.
*
* @param transform function that takes the index of an element and the element itself
* and returns the result of the transform applied to the element.
*/
public static List mapIndexedNotNullToList( @This Iterable thiz, IndexedFunction transform )
{
return thiz.mapIndexedNotNullTo( new ArrayList(), transform );
}
/**
* Applies the given {@code transform} function to each element and its index in the original collection
* and appends only the non-null results to the given {@code destination}.
*
* @param transform function that takes the index of an element and the element itself
* and returns the result of the transform applied to the element.
*/
public static > C mapIndexedNotNullTo( @This Iterable thiz, C destination, IndexedFunction transform )
{
thiz.forEachIndexed( ( index, element ) ->
{
R result = transform.apply( index, element );
if( result != null )
{
destination.add( result );
}
} );
return destination;
}
/**
* Applies the given {@code transform} function to each element and its index in the original collection
* and appends the results to the given {@code destination}.
*
* @param transform function that takes the index of an element and the element itself
* and returns the result of the transform applied to the element.
*/
public static > C mapIndexedTo( @This Iterable thiz, C destination, IndexedFunction transform )
{
int index = 0;
for( T item : thiz )
{
destination.add( transform.apply( index++, item ) );
}
return destination;
}
/**
* Returns a list containing only the non-null results of applying the given {@code transform} function
* to each element in the original collection.
* @deprecated Use {@link #mapNotNullToList(Iterable, Function)} instead.
*/
@Deprecated
public static List mapNotNull( @This Iterable thiz, Function transform )
{
return thiz.mapNotNullTo( new ArrayList<>(), transform );
}
/**
* Returns a list containing only the non-null results of applying the given {@code transform} function
* to each element in the original collection.
*/
public static List mapNotNullToList( @This Iterable thiz, Function transform )
{
return thiz.mapNotNullTo( new ArrayList<>(), transform );
}
/**
* Applies the given {@code transform} function to each element in the original collection
* and appends only the non-null results to the given {@code destination}.
*/
public static > C mapNotNullTo( @This Iterable thiz, C destination, Function transform )
{
thiz.forEach( element ->
{
R result = transform.apply( element );
if( result != null )
{
destination.add( result );
}
} );
return destination;
}
/**
* Applies the given {@code transform} function to each element of the original collection
* and appends the results to the given {@code destination}.
*/
public static > C mapTo( @This Iterable thiz, C destination, Function transform )
{
for( T item : thiz )
{
destination.add( transform.apply( item ) );
}
return destination;
}
/**
* Returns a list containing all the elmeents from {@code fromIndex} (inclusive)
*/
public static List subList( @This Iterable thiz, int fromIndex )
{
return thiz.subList( fromIndex, -1 );
}
/**
* Returns a list containing the elmeents {@code fromIndex} (inclusive) to {@code toIndex} (exclusive)
*/
public static List subList( @This Iterable thiz, int fromIndex, int toIndex )
{
if( thiz instanceof Collection && ((Collection)thiz).isEmpty() )
{
return emptyList();
}
boolean toEnd = toIndex < 0;
if( thiz instanceof List )
{
//noinspection unchecked
return ((List)thiz).subList( fromIndex, !toEnd ? toIndex : ((List)thiz).size() );
}
ArrayList list = new ArrayList<>();
Iterator iter = thiz.iterator();
for( int i = 0; (toEnd || i < toIndex) && iter.hasNext(); i++ )
{
T elem = iter.next();
if( i >= fromIndex )
{
list.add( elem );
}
}
return list.optimizeReadOnlyList();
}
/**
* Performs the given {@code action} on each element, providing sequential index with the element.
*
* @param action function that takes the index of an element and the element itself
* and performs the desired action on the element.
*/
public static void forEachIndexed( @This Iterable thiz, IndexedConsumer action )
{
int index = 0;
for( T item : thiz )
{
action.accept( index++, item );
}
}
/**
* Join the elements together in a String separated by {@code separator}.
*/
public static String joinToString( @This Iterable thiz, CharSequence separator )
{
return thiz.joinTo( new StringBuilder(), separator ).toString();
}
/**
* Append the elements to {@code buffer} separated by {@code separator}.
*/
public static A joinTo( @This Iterable thiz, A buffer, CharSequence separator )
{
int count = 0;
try
{
for( T e : thiz )
{
if( count++ > 0 )
{
buffer.append( separator );
}
buffer.append( e.toString() );
}
}
catch( Exception e )
{
throw new RuntimeException( e );
}
return buffer;
}
/**
* Accumulates value starting with {@code demo} value and applying {@code operation} from left to right to current accumulator value and each element.
*
* The operation is terminal.
*/
public static R fold( @This Iterable thiz, R initial, BiFunction operation )
{
R accumulator = initial;
for( T element : thiz )
{
accumulator = operation.apply( accumulator, element );
}
return accumulator;
}
}