New features:
Predicate | boolean function, which takes some input |
Functional interface | Interface to pass behavior |
What's New in Java 8 | https://leanpub.com/whatsnewinjava8/read |
Behaviour parametrization |
Which type do new Java8 expressions have?
// Function. Gets Ranking. Returns Integer. Function<Ranking, Integer> f = ranking -> ranking.value; // Consumer - accepts sinle parameter. Returns nothing Consumer<Ranking> c = Ranking:: echo; // Predicate - boolean returning function Predicate<Ranking> isLarger2 = ranking -> ranking.value > 2; // MODELS static class Ranking{ int value; public static void echo(Object value){ System.out.println(String.format("Value is '%s'",value)); } }
You can pass a method as a function-parameter. For that the method-signature must define a functional interface as a paramter.
Functional Interface |
An interface with exactly one single abstract method. |
Comparator is a functional interface
and equals is a method from Object.class. |
Object is NOT a functional interface Object o = () -> {return 2;} // invalid Object in not a functional interface |
Functional Interfaces may be annotated with @FunctionalInterface to clarify what they are!
The generic functions have following names:
..Consumer | provides a method with input. No output |
..Supplier | provides output. No input. |
..Function | provides method with input. And with output. |
..Predicate | provides a boolean function |
..Operation | method with input and output of same type |
class Test{ public static void main(String[] args){ // passing myMethod, which may be used inside fun Test.function(Test::myMethod); } // As defined by class FunctionalInterface - you can pass any function in here, // which takes String as parameter AND // which returns boolean public void function(FunctionalInterface interface){ } static interface FunctionalInterface{ boolean function(String parameter); // defines a single function operating on String, returning boolean } static class MyClass{ static boolean myMethod(String input){ return true; } }
Identifier | Identifiers are used as names variables, functions. Identifiert
abcd123 |
Literals | Notations for constant values, build in types 123.33 |
Operators |
The full operator list is here: https://docs.oracle.com/javase/tutorial/java/nutsandbolts/opsummary.html Not operators: print return |
Expression | It contains:
Expressions: "String is an expression too" "Alan" + i System.out.print("ho") |
Statement |
More here: https://docs.oracle.com/javase/tutorial/java/nutsandbolts/expressions.html Statements: System.out.print("ho"); return "Alan" + i; |
1.
(parameters) -> expression
2.
(parameters) -> { statements; }
Lamda Examples:
() -> {} // valid 1 () -> "Raoul" // valid 1 () -> {return "Mario";} // valid 1 (Integer i) -> return "Alan" + i; // invalid. Valid version: (Integer i) -> {return "Alan" + i;} (String s) -> {"Iron Man";} // invalid. Valid version: (String s) -> {return "Iron Man";}
More valid examples:
(TYPE name) -> FUNCTION(name) (String s) -> s.toUpperCase() + " !!!" (Apple a) -> a.getWeigth() > 150
Compiler can deduce the input type from the functional interface.
So you can omit the type in lambdas.
legal usages of lamdas:
List<Apple> greenApples = filter(inventory, a -> "green".equals(a.getColor())); // a has no type. It becomes an Apple automagically
Methods can be references, to use them as lambdas:
Integer::parseInt // local method on class Apple::getWeight // static method on class new Apple()::getWeight // on instance
Supplier<Apple> c1 = Apple::new; // supplier takes no pramas, creates Apple. COnstructor is a supplier Apple a1 = c1.get(); // produce an apple by calling a suplier
Streams introduce internal collection navigation where you don't need to.
As a contra to extrnal navigation, where you explicitely iterate a collection.
Streams describe computations of Data sets,
whereas collections describe the storage and access.
Operations on Streams are parallelized automagically, when using parallelStream()
inventory.stream().filter... // not parallelized inventory.parallelStream().filter... // parallelized
The stream methods are separated into:
intermediate | returns a stream. Which allows to execute the next operation, as defined in chain pattern. .stream().filter(...).distinct().limit(3)... |
terminal | operation which returns a non string. FInalizes the chain of stream commands. .stream()...count(); .stream()...collect(toList()); .stream()....forEach(System.out::println); |
Intermesiate methods
method | argument | result | mapping |
---|---|---|---|
filter | T→ boolean | Stream<T> | |
map | T → R | Stream<R> | |
limit | int | Stream<T> | |
skip | int | Stream<T> | |
sorted | T,T - int | Stream<T> | |
distinct | Stream<T> | ||
flatMap | Stream<T[]> → Stream<T> |
terminal methods
method | argument | result |
---|---|---|
forEach | ||
count | int | |
collect | ||
allMatch | boolean | |
anyMatch | boolean | |
noneMatch | boolean | |
findFirst | Optional<T> | |
findAny | Optional<T> | |
reduce | T, T → T | Optional<T> |
All the stream-methods chained one after another are executed in a single, internal iteration.
When you apply a sequence of operations to a stream, these are merely saved up. Only when you apply a terminal operation to a stream is anything actually computed. This has the great advantage when you apply several operations (perhaps a filter and a map followed by a terminal operation reduce) to a stream, then the stream has to be traversed only once instead of for each operation.
THe following code would produce the floowing output:
code
dishes.stream().filter(a -> { System.out.println(a.name); return true; }).distinct().map(a -> { System.out.println(a.type); return a.name; });
output
pork // name flesh // type pumkin // name vegetables // type
Name and type come one after another or each element.
This means that steps during parallel execution - methods that can block the parallel execution should not be executed inside the Stream-pipeline.
Assume you have two versions of getting prices from futures:
// directly chaining map methods to create Future, get Result is wrong. Because f.get() will be called after every future creation and block further Future creation (Single Pass) stream.map(Helper::createFuture).map((Future f) -> g.get()).asList() // collecting futures into collection first is the right parallel way. When we do the first "get" all Futures already exist and computing List<Future> futures = stream.map(Helper::toFuture).asList(); List<Integer> prices = futures.asStream().map((Future f) -> g.get()) On the Image the upper Situation is depicted
// filling the stream with content using "iterate" Stream.iterate(0l, i -> i + 1) .limit(100);
There are plently of collectors, which are availalbe via java.util.stream.Collectors. Just import all static collector methods and vars to use them by name.
import static java.util.stream.Collectors.*;
Glossar
multilevel reduction | groupingBy collector creates substreams, for different grouping criteria. Substreams may be transfered/grouped by another collector, called a downstream collector |
downstream collector | collector, to transfer substreams (substreams created by groupingBy collector) |
reduce-accumulator | method B, A → B , which converts an STREAM-ENTRY of type A to B. It is applied on EVERY Stream Element. It is passed the accumulated reduce value B so far and the next stream value A. It returns the value, resulting from accumulation of the next stream-element A |
reduce-combiner | when stream is reduced in a parallel way - combiner will combine every result, accumulated on different Streams. |
Method | Returns | Describtion | Example |
---|---|---|---|
toList | List<T> | Gather all the stream’s items in a List. | List<Dish> dishes = menuStream.collect(toList()); |
toSet | Set<T> | Gather all the stream’s items in a Set, eliminating duplicates. | Set<Dish> dishes = menuStream.collect(toSet()); |
toCollection | Collection<T> | Gather all the stream’s items in the collection created by the provided supplier. | Collection<Dish> dishes = menuStream.collect(toCollection(), ArrayList::new) |
counting | Long | long howManyDishes = menuStream.collect(counting()); |
|
summingInt | Integer | int totalCalories = menuStream.collect(summingInt(Dish::getCalories)); |
|
averagingInt | Double | double avgCalories = menuStream.collect(averagingInt(Dish::getCalories)); |
|
summarizingInt | IntSummary-Statistics | Collect statistics regarding an Integer property of the items in the stream, such as the maximum, minimum, total, and average. | IntSummaryStatistics menuStatistics = menuStream.collect(summarizingInt(Dish::getCalories)); |
joining | String | String shortMenu = menuStream.map(Dish::getName).collect(joining(", ")); |
|
maxBy / minBy | Optional<Dish> lightest = menuStream.collect(minBy(comparingInt(Dish::getCalories))); |
||
reducing | The type produced by the reduction operation | Reduce the stream to a single value starting from an initial value used as accumulator and iteratively combining it with each item of the stream using a BinaryOperator. | int totalCalories = menuStream.collect(reducing(0, Dish::getCalories, Integer::sum)); |
collectingAndThen | The type returned by the transforming function | int howManyDishes = menuStream.collect(collectingAndThen(toList(), List::size)); |
|
groupingBy | Map<K, List<T» | Maps the stream to groups (as hashmap keys) | Map<Dish.Type, List<Dish>> dishesByType = menuStream.collect(groupingBy(Dish::getType)); |
partitioningBy | Map<Boolean, List<T» | Map<Boolean, List<Dish>> vegetarianDishes =menuStream.collect(partitioningBy(Dish::isVegetarian)); |
/* COLLECTOR * * Collector<T,A,R> * * T - Type of input item in stream * A - intermediate Type of Objects, during calculation * R - Result Type * * e.g. Collector which collects Stream of Strings to List * T - String * A - List<String> * R - List<String> * * Supplier<A> supplier() - provides initial value of intermediate Type * BiConsumer<A,T> accumulator() - input -> the A value calculated so far. T is the next value in stream * Function<A,R> finisher() - function is executed after iteration over all elements is done * BinaryOperator<A> combiner() - function which combined two intermediate Objects. This one makes multithreading possible * Set<Characteristics> characteristics - some optimization hints. UNORDERED, CONCURRENT, IDENTITY_FINISH * * * supplier - ArrayList::new * accumulator - (List<String> a, String b) -> {a.add(b)} * finisher - Functions::identity * combiner - (List<String> a, List<String> b) -> {a.addAll(b)} * characteristics - return Collections.unmodifyableSet(EnumSet.of(IDENTITY_FINISH, CONCURRENT )) */
Boxed Values are a performace killer. Using a Stream with primitives - improves performance by factor x6
long max = 90000000; // LongStream uses primitives (long) start = System.currentTimeMillis(); summ = LongStream.iterate(0l, i -> i + 1) .limit(max) .reduce(0l, Long::sum); System.out.println("Sequential LongStream Ready filling after: "+ (System.currentTimeMillis()-start) +"ms"); // 220ms // Stream generates Boxed Values (Long) start = System.currentTimeMillis(); summ = Stream.iterate(0l, i -> i + 1) .limit(max) .reduce(0l, Long::sum); System.out.println("Sequential Stream Ready filling after: "+ (System.currentTimeMillis()-start) +"ms"); // 1228 ms
Reducing DIshes to Strings in a parallel way:
// parallel streams of Dishes, reduced to String, which is a concatenated list of names String res = getExampleStream().parallel().reduce("", new BiFunction<String, Dish, String>() { @Override public String apply(String t, Dish u) { return t+u.name; } //accumulator - can convert the Stream content to something else (String) }, (a,b) -> { return a+b; } // combiner - used in the case of using parallel streams );
It is possible to implement default methods in interfaces.
So multiple inheritance is some sort of allowed now in java!!!
The default interface methods are marked by keyword default
default void methodName(){...
public class Pazak implements HasDefaultMethodKu, HasDefaultMethodKuToo{ // have to override method #ku() to explicetly say which interface's method to execute @Override public void ku() { HasDefaultMethodKu.super.ku(); } } public interface HasDefaultMethodKu{ default void ku(){ System.out.println("Say Ku!"); } } public interface HasDefaultMethodKuToo{ default void ku(){ System.out.println("Say Ku Too!"); } }
Return that instead of null to avoid NPE
Methods
isPresent | |
ifPresent(Consumer<T> block) | |
get | value or NoSuchMethod-Exception |
T orElse(T other) |
public class Optionals { String name = "Сапожник козоед"; public static void main(String[] args) { Optionals optionals = new Optionals(); System.out.println(optionals.getName().get()); // add defaults System.out.println(optionals.getName().orElse("defaultName")); System.out.println(optionals.getEmptyName().orElse("defaultName")); } Optional<String> getName(){ return Optional.of(name); } Optional<String> getEmptyName(){ // explicitly say that this var is nullable return Optional.ofNullable(null); // return Optional.of(null); // would produce a NullPointerException } }
Like a future with more functional-style interface.
CompletableFuture<Double> f = CompletableFuture.supplyAsync(() -> calculatePrice());
The occured Exceptions are rethrown on get()
CompletableFuture<Double> f = ... try { double calculatedPrice = f.get(); System.out.println(String.format("Everything was fine. Price calculated: "+calculatedPrice)); return calculatedPrice; } catch (Exception e1) { System.out.println(String.format("I know - an Exception occured earlier on another thread: %s.", e1.getCause())); return (double) 0; }
The CompletableFuture can be chained, to execute some code when the future has completed the computation.
CompletableFuture<Integer> c = null; CompletableFuture<String> c2 = null; CompletableFuture cc = c.thenApply((Integer i) -> "Done"); /* thenApply((FutureType) -> T) -> CompletableFuture<T> * Executed, when previous Future is done. * Modifies the value "INSIDE" the completableFuture container to T */ /* thenRun(() -> Void) -> CompletableFuture<Void> * Executes some code (no params are passed), when previous Future is done */ /* thenAccept(FutureType -> Void) -> CompletableFuture<Void> * Executed, when the previous Future is done * VOID will be inside the Future after this call. To preserve some Type T in FUture - use thenApply() */ CompletableFuture ccc = c.thenCompose((Integer inte) -> CompletableFuture.runAsync(() -> System.out.println("Done"))); /* thenCompose(FutureType) -> CompletableFuture) -> CompletableFuture * Executes a SECOND CompletableFuture, when the first is done */ // thenCombine CompletableFuture<Double> aaa = c.thenCombine(c2, (Integer ii, String ss) -> 2.2); /* thenCombine(CompletableFuture, Function(FutureType1, FutureType2) -> T) -> CompletableFuture<T> * Executes FIRST and SECOND ComputableFutures. Produces the results in a Function which itselfe produces a ComputableFunction */
Chaining can be used in streams, to implement real parallel (not only concurrent) evaluation.
// List<Picture> -> List<Futures<Picture>> (List<Picture> l) -> { List<CompletableFuture<Picture>> listFuturesFIlledPrice = l .stream() // Picture -> Futures<Picture> // echo result of future when ready .map((Picture picturePriceless) -> { CompletableFuture<Picture> picFutureWithPrice; // ASYNC DELAYED: get price picFutureWithPrice = CompletableFutures.getFutureFillingPriceInPictureDelayed(picturePriceless); // react immediately after calculation is done picFutureWithPrice = picFutureWithPrice.thenApply(CompletableFutures::echoPictureWhenFutureReady); return picFutureWithPrice; } ) .collect(Collectors.toList()); return listFuturesFIlledPrice; }
Basic classes
Instant |
// INSTANT - to be used by machines Instant instant = Instant.ofEpochSecond(1454523011); // nanosecond adjustment - 1sec=1.000.000.000nanosec Instant.ofEpochSecond(0); // 1.January.1970 UTC Instant.ofEpochSecond(2, 1000_000_000); // 2sec + 1sec Instant.ofEpochSecond(3, -1000_000_000); // 4sec - 1sec // Instant -> LocalDate, LocalTime LocalDate dateOfInstant = LocalDateTime.ofInstant(instant, ZoneId.systemDefault()).toLocalDate(); LocalTime timeOfInstant = LocalDateTime.ofInstant(instant, ZoneId.systemDefault()).toLocalTime(); | |
LocalDate |
LocalDate localDateNow = LocalDate.now(); LocalDate localDateNowLondon = LocalDate.now(ZoneId.of("UTC+0")); LocalDate localDate = LocalDate.of(2016, 2, 16); int fieldYear = localDate.get(ChronoField.YEAR); | |
LocalTime |
LocalTime time = LocalTime.of(14, 55, 54, 11 ); // hour, minute, second, milli LocalTime time2 = LocalTime.of(14, 55, 54 ); // hour, minute, second LocalTime time3 = LocalTime.of(14, 55 ); // hour, minute int hourTime = time.getHour(); int minuteTime = time.getMinute(); | |
LocalDateTime |
// 1415-07-6T6:55:20 LocalDate dateOne = LocalDate.of(1415, Month.JULY, 6); LocalTime timeOne = LocalTime.of(6, 55, 20); LocalDateTime dt1 = LocalDateTime.of(2014, Month.MARCH, 18, 13, 45, 20); LocalDateTime dt2 = LocalDateTime.of(dateOne, timeOne); LocalDateTime dt3 = dateOne.atTime(13, 45, 20); LocalDateTime dt4 = dateOne.atTime(timeOne); LocalDateTime dt5 = timeOne.atDate(dateOne); | |
ZoneId |
ZoneId romeZone = ZoneId.of("Europe/Rome"); ZoneId berlinZone = ZoneId.of("Europe/Berlin"); ZoneId myZone = TimeZone.getDefault().toZoneId(); | |
ZonedDateTime |
ZoneId berlinZone = ZoneId.of("Europe/Berlin"); ZonedDateTime zonedDateTimeBerlin1 = dateOne.atStartOfDay(berlinZone); ZonedDateTime zonedDateTimeBerlin2 = localDateTimeOfGuss.atZone(berlinZone); | |
DateTimeFormatter |
LocalDate parsedDate; parsedDate = LocalDate.parse("2014-03-18"); parsedDate = LocalDate.parse("2014-03-18", DateTimeFormatter.ISO_DATE); parsedDate = LocalDate.parse("18/03/2014", DateTimeFormatter.ofPattern("dd/MM/yyyy")); parsedDate = LocalDate.parse("18.03.2014", DateTimeFormatter.ofPattern("dd.MM.yyyy")); parsedDate = LocalDate.parse("18 März 2014", DateTimeFormatter.ofPattern("dd MMMM yyyy", Locale.GERMAN)); DateTimeFormatter germanFormatter = new DateTimeFormatterBuilder() .appendText(ChronoField.DAY_OF_MONTH) // 6 .appendLiteral(".") .appendValue(ChronoField.MONTH_OF_YEAR) // 7 .appendLiteral("( alias ") .appendText(ChronoField.MONTH_OF_YEAR) // July .appendLiteral(")") .appendLiteral(".") .appendValue(ChronoField.YEAR) // 1941 .parseCaseInsensitive() .toFormatter(Locale.GERMAN); System.out.println(germanFormatter.format(dateOne)); |
|
Duration |
/* * Duration is CRAP, because based on Seconds / Nanos. * - you only can compute a duration on "time based units" - something what can handle seconds like LocalTime. * The compiler still allows computing Duration between LocalDate, or getting YEARS from Duration, * which results in an Exception. * WHY OR WHY CAN'T I JUST CONVERT SECONDS IN ANY UNIT WITHOUT EXCEPTIONS, AS EXPECTED?! */ // Duration only works with Seconds-supporting types. Using LocaDate - throws an exception // Duration durationSinceGuss = Duration.between(dateOne, LocalDate.now()); Duration durationSinceGuss1 = Duration.between(LocalDateTime.of(dateOne, LocalTime.MIDNIGHT),LocalDateTime.now()); Duration durationSinceGuss2 = Duration.between(timeOne,LocalTime.now()); // Duration durationSinceGuss3 = Duration.between(dateOne,LocalDate.now()); // exception. It trys to convert LocalDate to Seconds, which is not allowed | |
Period |
Period periodBetweenGuss = Period.between(dateOne, LocalDate.now()); long yearsSinceGuss7 = periodBetweenGuss.getYears(); | |
TemporalAdjuster |
LocalDate dateNextSunay = dateOne.with(TemporalAdjusters.next(DayOfWeek.SUNDAY)); |
The Time Units are contained by ChronoUnit.class and separated in timeBased and dateBased:
It is FUCKING UNCLEAR, why DAYS ARE NOT TIMEBASED?! Still it is important, some operations are only legal for time- or dateBased units: like getting the amount of years from a Duration.
TimeBased | DateBased |
---|---|
Nanos | Days |
Micros | Weeks |
Millis | Months |
Seconds | Years |
Minutes | Decades |
Hours | Centuries |
HalfDays | Millennia |
Eras |
The same separation exists with Classes:
TimeBased | DateBased |
---|---|
LocalDateTime | LocalDateTime |
LocalDate | LocalTime |
Period | Duration |
mixing them up produces an UnsupportedTemporalTypeException.
localDate.plus(1, CHronoUnit.SECOND) // UnsupportedTemporalTypeException localTime.plus(1, CHronoUnit.DAY) // UnsupportedTemporalTypeException // LocalDateTime supports both localDateTime.plus(1, ChronoUnit.SECONDS); localDateTime.plus(1, ChronoUnit.DAYS);
CHronofield - contains diffrent kinds of data info names, which may be requested from diffrent TemporalFields.
int fieldYear = localDate.get(ChronoField.YEAR); /* ChronoField. NanoOfSecond NanoOfDay MicroOfSecond MicroOfDay MilliOfSecond MilliOfDay SecondOfMinute SecondOfDay MinuteOfHour MinuteOfDay HourOfAmPm ClockHourOfAmPm HourOfDay ClockHourOfDay AmPmOfDay DayOfWeek AlignedDayOfWeekInMonth AlignedDayOfWeekInYear DayOfMonth DayOfYear EpochDay AlignedWeekOfMonth AlignedWeekOfYear MonthOfYear ProlepticMonth YearOfEra Year Era InstantSeconds OffsetSeconds */
How to write code in Java8?
You can replace many anonymous classes with method calls. Important:
theme | anonymous class | lambdas |
---|---|---|
this | means the anonymous class | means the wrapping class |
overriding vars | is allowed | is forbidden |
typing | is unambigous | may become ambigous - needs explicit typing |
Runnable runnable = new Runnable(){ void execute(){ System.out.println("Go!") } } Runnable runnable = () -> System.out.println("Go!"); // ok int mVar = 20; Runnable runnable = () -> { int mVar = 30; // not ok - overriding vars not allowed System.out.println("Go!"); } public static void doSomething(Runnable r){ r.run(); } public static void doSomething(Task a){ r.execute(); } doSomething(() -> System.out.println("Go!")); // not ok - call doSOmething for Task or Runnable? doSomething((Task)() -> System.out.println("Go!")); // ok
Stream<String> stream = ... stream.map(String::length); stream.map(Helpers::quote); String quote(String str){ return String.format("'%s'",str); }
Native collectors are known by others. Are better readable.
int totalCalories = menu.stream().collect(summingInt(Dish::getCalories));
Better readability, optimization behind the scenes.
// collecting names of dishes, which have > 300 calories List<String> dishNames = new ArrayList<>(); for (Dish dish : menu) { if (dish.getCalories() > 300) { dishNames.add(dish.getName()); } } // intent is better readable. menu.parallelStream() .filter(d -> d.getCalories() > 300) .map(Dish::getName) .collect(toList());
// message is evaluated EVERY TIME, even if the level does not fit logger.log(Level.FINER, "Problem: " + generateDiagnostic()); // to avoid - pass a lambda, which will be called ONLY IF LEVEL FITS logger.log(Level.FINER, () -> "Problem: " + generateDiagnostic());
// same code, which is executed around some variable logic void method1(){ doPreparation(); //variable method here varMethod2(); cleanUp(); } void method2(){ doPreparation(); //variable method here varMethod2(); cleanUp(); } // better void method(Runnable r){ doPreparation(); r.execute(); cleanUp(); } method(()->varMethod1()); method(()->varMethod2());
With Java8 the programming style is pushing towards the functional edge.
Some rules for writing functional code:
Requirenments to functions
Functions may only change private variables.
Calling same function always returns the same result.
E.g. readNextLine() is NOT referential transparent. It alwas returns another line.
What to do, when programming:
pass functions to methods. Return new functions with functionality around it.
Function<String, String> transformationPipeline = addHeader.andThen(Letter::checkSpelling) .andThen(Letter::addFooter);
create FUnctions producing FUnctions,
which will prefill some constants.
// carrying Function createConvertingFunction(double course){ return (double x) -> course * x; } Function eurToChf = createConvertingFunction(0.9); Function bintcoinToEur = createConvertingFunction(421);
When function reuses another function and
when preselecting some arguments (but not all) have been passed -
then the function is partially applied
Function echoColoredObject(Object object, String color){ (object, color) -> System.out.println("The "+object+" has the color "+color); } // partially apply function echoColoredObject Function echoColoredBall(String color){ (color) -> echoColoredObject("Ball", color) }
If you would like to create a method accepting a lamda use the Argument, typed with one of those methods: java.util.function.IntBinaryOperator.
Examples:
Consumer<Double> c = aDouble -> System.out.println(aDouble); // no output Function<Double, String> f = aDouble -> String.valueOf(f); // function returns what you define DoubleFunction<String> d = doubleValue -> String.valueOf(doubleValue); // same as previous IntBinaryOperator plusOperation = (a, b) -> a + b; // operator on two int numbers a and b LongPredicate l = longValue -> longValue < 0l; // boolean function
List<Integer> list = List.of(1, 2, 3, 4, 9, 8, 7, 6, 1, 2, 3); // gives maybe 7, 8 or 9, cause findAny doesnt respect order int res = list .stream() .parallel() .filter(integer -> integer > 6) .findAny() .orElseThrow(); assertThat(res, anyOf(is(7), is(8), is(9))); // gives 9, cause its the first in the row match to filter int res2 = list .stream() .parallel() .filter(integer -> integer > 6) .findFirst() .orElseThrow(); assertThat(res, is(9));
// cruel method to concat numbers, just to demonstrate reduce List<Integer> list = List.of(1, 2, 3, 4, 9, 8, 7, 6, 1, 2); Integer res = list .stream() .mapToInt(value -> value) .reduce(0, (integer, integer2) -> { return Integer.parseInt(String.format("%s%s", integer, integer2)); } ); System.out.println(res); // 1234987612 // better way would be with java8 String res2 = list .stream() .map(String::valueOf) .collect(Collectors.joining()); System.out.println(res2); // 1234987612