import static extension de.fhg.fokus.xtensions.iteration.IterableExtensions.*
import java.util.OptionalDouble
//...
#["foo", null, "BAR", "bazzzz"]
.filterNull
.averageSize
.ifPresent [
println('''The average string lenght is «it»''')
]
//...
private def OptionalDouble averageSize(Iterable<String> strings) {
strings.stream.mapToInt[length].average (1)
}
Extensions to Iterable
The de.fhg.fokus.xtensions.iteration.IterableExtensions
class provides extension methods to
java.lang.Iterable
Unfortunately the java.lang.Iterable
interface does not provide a (default)
method for creating a java.lang.Stream
. It does provide a method to obtain a
Spliterator
which can be used to create a stream, but this is rather unpleasant to use.
The IterableExtensions
class provides the stream
extension method to easily create
a stream from an iterable. This method will first check if the given iterable is instance of
java.util.Collection
, since this class does provide a default stream
method,
otherwise it will construct a stream from the spliterator provided by the iterable.
Example:
-
In this line the extension method
stream
is called on the iterablestrings
.
Analogous to the stream
method the IterableExtensions
class also provides a parallelStream
method.
It is also possible to map an iterable to a primitive iterable (see Primitve Iterables / From Iterables).
The JDK since Java 8 provides the class java.util.stream.Collector
which can be used with streams
to perform a reduction operation over all elements in a stream. The class java.util.stream.Collectors
already provides constructor methods for a bunch of useful collectors. The IterableExtensions
class
of this library provides a collect
extension method directly for Iterable
to easily reduce the elements
of the iterable.
Example:
import static java.util.stream.Collectors.*
import static extension de.fhg.fokus.xtensions.iteration.IterableExtensions.*
// ...
val Iterable<String> strings = #["fooooo", "baar", "baz"]
val summary = strings.collect(summarizingInt[length])
println("Average length: " + summary.average)
println("Max length: " + summary.max)
A fairly common task in Model-to-Model transformations is to find objects of one type that are not referenced by some other objects. This is usually done by navigation over all elements in a model, finding all elements of both types and then finding the elements that are actually not referenced. This is typically done by traversing the complete model twice to find the elements of both types. This can be an expensive operation if the input model is large. The solution is to traverse the model just once and group the elements according to their type.
This library provides a method allowing exactly this. The method groupIntoListBy
and groupIntoSetBy
groups elements of an iterable by their type.
Example:
val foo = "foo"
val bar = "bar"
val baz = "baz"
val traverseMe = #[foo, #[bar], baz, #[foo], bar]
val groups = traverseMe.groupIntoSetBy(String, List)
val Set<String> strings = groups.get(String)
val Set<List> lists = groups.get(List)
val inNoList = strings
.filter[str|
!lists.exists[it.contains(str)]
].toList
println("Elements contained in no list: " + inNoList)
To exclude elements of one Iterable
from another, the method withoutAll
can be used.
val s = #["I", "boo", "pity", "char", "the", "fool"]
.withoutAll(#{"boo", "char"})
.join(" ")
println(s)
Caution
|
Performance of withoutAll is best when using an appropriate java.util.Set , such as HashSet for the elements to exclude.
|
To partition the elements of an Iterable
based on a predicate or the class (elements are tested to be instance of) into two parts:
selected and rejected. The selected part will contain the elements for which the predicate evaluates to true
or elements are instance
of the given partitioning class. The rejected part will contain the other elements.
Example:
import static extension de.fhg.fokus.xtensions.iteration.IterableExtensions.*
// ...
val char[] chars = #[0x0020 as char, 0x0034 as char]
val List<CharSequence> list = #[
"Hello",
new StringBuilder().append(chars),
"Xtend",
new StringBuilder().append(0x0032 as char)
]
list.partitionBy(String) => [
println('''Selected: "«selected.join(" ")»"''')
println('''Rejected: "«rejected.join("")»"''')
]
For both versions of the partitionBy
method there exist overloads that take instances of Collector
.
Example:
import static extension de.fhg.fokus.xtensions.iteration.IterableExtensions.*
import static java.util.stream.Collectors.*
import java.util.Set
import java.util.List
// ...
val list = #["foo", "bla", "foo", "hui", "fun"]
val partition = list.partitionBy([startsWith("f")], toSet, toList)
val Set<String> selected = partition.selected
val List<String> rejected = partition.rejected
println("Unique words starting with 'f' : " + selected.join(", "))
println("Other words: " + rejected.join(", "))
Note that the Collectors#partitioningBy
Collector from the JDK aggregates into a Map<Boolean,List<T>>
where T
is the type of the elements in a collected stream. Another partitioningBy
overload from Collectors
aggregates the map values and returns a Map<Boolean,D>
where D
is the aggregation of a "downstream" Collector
.
import static java.util.stream.Collectors.*
import java.util.Set
// ...
val list = #["foo", "bla", "foo", "hui", "fun"]
val partition = list.stream.collect(partitioningBy([startsWith("f")], toSet))
val Set<String> selected = partition.get(true)
val Set<String> rejected = partition.get(false)
println("Unique words starting with 'f' : " + selected.join(", "))
println("Other words: " + rejected.join(", "))
To allow a similar workflow to the JDK version, for the Partition
of this library a asMap
extension method is provided
for Partitions
having the same type for the selected and rejected part. The other way around an extension method is provided
to wrap a Map<Boolean,X>
into a Partition<X,X>
.
To add elements from an Iterable
to one or more collections, the into
extension method is provided by the class IterableExtensions
.
Example:
val namesWithB = newArrayList("Barbara", "Bob", "Brian")
val newNames = #["Justin", "Anna", "Bruce", "Chris", "Becky"]
newNames
.filter[it.toFirstLower.startsWith("b")]
.into(namesWithB)
namesWithB.forEach[
println(it)
]
Tip
|
Related JavaDocs: |