User Tools

Site Tools


java_api_8

This is an old revision of the document!


A PCRE internal error occured. This might be caused by a faulty plugin

====== Stream API ====== The ''stream'' API is a step in the field of functional programming, that is a different programming style that is more declarative. It is actually much more similar to SQL than to OOP. At any rate, Java introduced the ''Stream API'' and the ''lambda'' to go in this direction. ===== The pipeline ===== A ''stream'' is a pipeline to elaborate data. It provides the normal control-flow mechanism in a different way. A ''stream'' is characterized by 3 elements: * a start (where we supply data), * a middle (elaboration), * an end (where we collect the data). ==== Begin ===== The beginning of the pipeline must provide the data. Streams can be obtained in a number of ways. Some examples include: * From a ''Collection'' via the ''stream()'' and ''parallelStream()'' methods; * From an array via ''Arrays.stream(Object[]);'' * From static factory methods on the stream classes, such as ''Stream.of(Object[])'', ''IntStream.range(int, int)'' or ''Stream.iterate(Object, UnaryOperator)''; * The lines of a file can be obtained from ''BufferedReader.lines()''; * Streams of file paths can be obtained from methods in ''Files''; * Streams of random numbers can be obtained from ''Random.ints()''; * Numerous other stream-bearing methods in the JDK, including ''BitSet.stream()'', ''Pattern.splitAsStream(java.lang.CharSequence)'', and ''JarFile.stream()''. Some examples: <code java> String[] stringArr = {"a", "b", "c", "d"}; Stream<String> stream = Arrays.stream(stringArr); // from arrays Collection<String> list = new Arrays.asList("one" , "two" , "three"); Stream<String> s1 = list.stream(); // from collections Stream<String> s2 = Stream.generate(()->"generate"); // a continuos stream generated by a Supplier // functional interface with get abstract method Stream<String> s3 = Stream.iterate("0" , i -> ++(char)i); Stream<Integer> s4 = Stream.iterate(0, i-> ++i); // generates or better iterates from natural numbers. // It Autoboxes so it does not perform well. // Iterating on numbers it's better // done with appropriate primitive streams. IntStream is5 = IntStream.iterate(0, i -> ++i); // notice that the lambda has a pre-increment otherwise an // infinite stream of 0-s is generated IntStream s6 = IntStream.iterate('0', x -> ++x); s6.limit(25).mapToObj(x->((char)x)).forEach(x->System.out.print(x + " ")); </code> ==== Intermediate Operations (Elaboration) ===== From [[https://docs.oracle.com/javase/8/docs/api/java/util/stream/package-summary.html#StreamOps | javadoc]]: Intermediate operations return a new stream. They are always lazy; executing an intermediate operation such as filter() does not actually perform any filtering, but instead creates a new stream that, when traversed, contains the elements of the initial stream that match the given predicate. Traversal of the pipeline source does not begin until the terminal operation of the pipeline is executed. ==== Terminal Operation / Collection ==== A stream is lazily created and executed. So it won't work unless you terminate it. Termination change the stream to something more usable like a List or a Number or whatever you need. Most common terminations are: * ''collect()'', it collects the result of the stream into a mutable objects as Collection * ''reduce()'', reduce the stream to a single object based on some rules defined with functional interfaces. * find matches: they use a ''Predicate<? super T>'' to determine the match. * ''allMatch()'', returns whether **all** elements of this stream match the provided predicate. May not evaluate the predicate on all elements if not necessary for determining the result. * ''anyMatch()'', returns whether **any** elements of this stream match the provided predicate. May not evaluate the predicate on all elements if not necessary for determining the result. If the stream is empty then false is returned and the predicate is not evaluated. * ''noneMatch()'', returns whether **no** elements of this stream match the provided predicate. May not evaluate the predicate on all elements if not necessary for determining the result. * ''findAny()'', returns an ''Optional'' describing some element of the stream, or an empty ''Optional'' if the stream is empty. * ''findFirst()'', returns an ''Optional'' describing the first element of this stream, or an empty Optional if the stream is empty. If the stream has no encounter order, then any element may be returned. * ''forEach()'', iterates on the elements of the stream allowing some operation to be done, but does not return. **Hangs on infinite streams**. * ''min()'' ''max()'', determine the min and the max value in the stream according to an optional comparator. Return an Optional. **Hangs on infinite streams**. * ''count()'', determines the number of elements in the stream. **Hangs for infinite streams**. === reduce() === The signature is <code java> T reduce(T identity, BinaryOperator<T> accumulator) </code> It uses a ''BinaryOperator'' functional interface to accumulate the result, by setting an initial value (''identity''). It has 2 inputs and 1 output of the same type. You can use it, for example to calculate the ''pi=3.14...'' with the [[https://en.wikipedia.org/wiki/Leibniz_formula_for_%CF%80 | Gregory-Leibniz series]] <code java GregoryLeibniz.java> import java.util.*; import java.util.function.Supplier; import java.util.stream.*; public class GregoryLeibnitz { public static void main(String[] args) { int limit = 1000; Double pi = 4 * IntStream.iterate(0, n -> n+1) .limit(limit) // limit the infinite stream .mapToDouble(v -> (double)v) .reduce(0., //identity, i.e. the starting point (a,x) -> a + (x%2==0?1:-1) / (2*x+1) //BinaryOperator<Double> ); System.out.println("pi= " + pi); // 3.140592653839794 } } </code> There is another signature: <code java> Optional<T> reduce(BinaryOperator<T> accumulator) </code> by which you do not pass the ''identity'' and you don't get the right type ''T'' but an ''Optional<T>''. [[https://docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.html#reduce-U-java.util.function.BiFunction-java.util.function.BinaryOperator- | In this case the first value in the accumulator is the first value passed to it]]. <code java> OptionalInt sum = IntStream.iterate(1, n->n+1) .limit(3) .reduce((a,x) -> a + x); System.out.println("sum " + sum.toString()); // sum OptionalInt[6] System.out.println("sum " + sum.getAsInt()); // sum 6 // notice that it sums in this way: // x=1 a=1 // first value is the first element // x=2 a=1+2 // x=3 a=3+3 </code> A last [[https://docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.html#reduce-U-java.util.function.BiFunction-java.util.function.BinaryOperator- | option]] is: <code java> <U> U reduce(U identity, BiFunction<U,? super T,U> accumulator, BinaryOperator<U> combiner) </code> Performs a reduction on the elements of this stream, using the provided identity, accumulation and combining functions. Generally used with parallel streams, it is like splitting the reduction in 2 steps. The identity value must be an identity for the combiner function. This means that for all u, combiner(identity, u) is equal to u. Additionally, the combiner function must be compatible with the accumulator function; for all u and t, the following must hold: combiner.apply(u, accumulator.apply(identity, t)) == accumulator.apply(u, t) ==== collect() ==== The [[https://docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.html#collect-java.util.function.Supplier-java.util.function.BiConsumer-java.util.function.BiConsumer- | javadoc]] is pretty explanatory of the first signature: <code java> <R> R collect(Supplier<R> supplier, BiConsumer<R,? super T> accumulator, BiConsumer<R,R> combiner) </code> This produces a result equivalent to: <code java> R result = supplier.get(); for (T element : this stream) accumulator.accept(result, element); return result; </code> So with the ''supplier'' you create an object where to store the result (e.g. an ArrayList), the accumulator calls the functional interface that is a ''BiConsumer'' and gets 2 elements and combines them (e.g. ''.add()'' in an ArrayList). Lastly, if the operation is performed in parallel the combiner combines the result together in another type ''R'' container by using its ''accept'' method. For example, the following will accumulate strings into an ArrayList: <code java> List<String> asList = stringStream.collect(ArrayList::new, ArrayList::add, ArrayList::addAll); </code> The following will take a stream of strings and concatenates them into a single string: <code java> String concat = stringStream.collect(StringBuilder::new, StringBuilder::append, StringBuilder::append) .toString(); </code> ===== Example ===== <code java StreamExercise.java> import java.util.*; import java.util.stream.*; public class StreamExercise { public static void main(String[] args) { // calculate the sum of an array List<Integer> list = Arrays.asList(0,1,2,3); int thesum = list.stream() //create the stream .reduce(0,(a,x)->a+x); // elaborate and return System.out.println(thesum); // 6 // data from a Supplier Stream<Integer> s = Stream.iterate(0, n -> n+1); Integer i = s.limit(4) .reduce(0, (a,x) -> a + x ); System.out.println(i); // 6 } } </code>

java_api_8.1478096328.txt.gz · Last modified: 2016/11/02 14:18 by 178.237.8.52