http://massivetechinterview.blogspot.com/2018/04/java-8-stream-2.html
http://www.deadcoderising.com/2015-05-19-java-8-replace-traditional-for-loops-with-intstreams/
https://github.com/aruld/java-oneliners/wiki
http://www.rowkey.me/blog/2017/09/09/java-oneliners/
https://www.leveluplunch.com/java/examples/java-util-stream-intstream-example/
Although not as extensive as guava's ranges or rangemaps, IntStream.range returns a sequential IntStream for the range of int elements. IntStream.range first parameter is inclusive while the second is exclusive.
http://www.codeaffine.com/2015/11/16/from-arrays-to-streams-and-back-with-java-8/
https://gist.github.com/verhas/a2ae93fc8ee14746b54df767e54ab9ce
https://medium.com/@hithacker/producing-immutable-list-from-stream-in-java-8-97f96ae3b04f
http://www.baeldung.com/java-8-collectors
https://www.javabrahman.com/java-8/java-8-how-to-use-collectors-collectingandthen-method-with-examples/
String maxSalaryEmp = employeeList.stream().collect(
Collectors.collectingAndThen(
Collectors.maxBy(Comparator.comparing(Employee::getSalary)),
(Optional<Employee> emp)-> emp.isPresent() ? emp.get().getName() : "none") );
http://www.logicbig.com/tutorials/core-java-tutorial/java-util-stream/lazy-evaluation/
https://bytefish.de/blog/jdk8_files_lines_parallel_stream/
In Java you can basically turn every Stream into a Parallel Stream by calling the
https://howtodoinjava.com/java-8/stream-max-min-examples/
https://stackoverflow.com/questions/24378646/finding-max-with-lambda-expression-in-java
https://stevewall123.wordpress.com/2014/08/31/java-8-streams-max-and-streams-min-example/
https://docs.oracle.com/javase/8/docs/api/java/util/stream/Collectors.html#toMap-java.util.function.Function-java.util.function.Function-
The
List evens = Arrays.asList(2, 4, 6); List odds = Arrays.asList(3, 5, 7); List primes = Arrays.asList(2, 3, 5, 7, 11); List numbers = Stream.of(evens, odds, primes) .flatMap(list -> list.stream()) .collect(Collectors.toList());
The function you pass to map() operation returns a single value.
The function you pass to flatMap() opeartion returns a Stream of value.
flatMap() is combination of map and flat operation.
map() is used for transformation only, but flatMap() is used for both transformation and flattening.
http://javarevisited.blogspot.com/2016/03/difference-between-map-and-flatmap-in-java8.html
is the combination of a map and a flat operation. This means you first apply map function and than flattens the result. Key difference is the function used by map operation returns a Stream of values or list of values rather than single value, that's why we need flattening. When you flat a Stream of Stream, it gets converted into Stream of values.
Before flattening - Stream of List of Integer
After flattening - Stream of Integer
https://www.mkyong.com/java8/java-8-flatmap-example/
int sum = Arrays.stream(myArray).sum();
String result = Arrays.stream(myArray)
.reduce("", (a,b) -> a + b);
http://stackoverflow.com/questions/31004899/java-8-collectors-tomap-sortedmap
https://www.mkyong.com/java8/java-8-convert-list-to-map/
Java 8 streams cannot be reused
http://stackoverflow.com/questions/33016329/cannot-use-java-8-method-with-lambda-arguments-without-specifying-type-arguments
http://www.leveluplunch.com/java/examples/group-count-repeating-characters-in-string/
http://stackoverflow.com/questions/22435833/why-is-string-chars-a-stream-of-ints-in-java-8
Map<Character, Long> map = "abcabc".chars().mapToObj(c -> (char) c)
.collect(Collectors.groupingBy(c -> c, Collectors.counting()));
http://stackoverflow.com/questions/23925315/count-int-occurrences-with-java8
Eclipse java 8 lamdba support sucks
Debugging
http://stackoverflow.com/questions/24541786/how-to-debug-stream-map-with-lambda-expressions
https://dzone.com/articles/dark-side-lambda-expressions
max = integers.parallelStream().reduce(Integer::max)
integers.stream().reduce(Integer.MIN_VALUE, (a, b) -> Integer.max(a, b));
https://www.reddit.com/r/java/comments/2suvir/java_8_lambda_performance_is_not_great/
http://blog.codefx.org/java/stream-performance/
http://blog.codefx.org/java/stream-performance/
1. Performance – you will lose on it
list.forEach(System.out::println);
Filter
https://www.mkyong.com/java8/java-8-streams-filter-examples/
http://stackoverflow.com/questions/22601036/stream-from-two-dimensional-array-in-java
Stream from two dimensional array in java
https://blog.jooq.org/2014/06/13/java-8-friday-10-subtle-mistakes-when-using-the-streams-api/
1. Accidentally reusing streams
2. Accidentally creating “infinite” streams
IntStream.iterate(0, i -> i + 1)
.forEach(System.out::println);
3. Accidentally creating “subtle” infinite streams
4. Accidentally creating “subtle” parallel infinite streams
IntStream.iterate(0, i -> ( i + 1 ) % 2)
.parallel()
.distinct()
.limit(10)
.forEach(System.out::println);
5. Mixing up the order of operations
IntStream.iterate(0, i -> i + 1)
.limit(10) // LIMIT
.skip(5) // OFFSET
.forEach(System.out::println);
7. Walking the file system with filters
Files.walk(Paths.get("."))
.filter(p -> !p.toFile().getName().startsWith("."))
.forEach(System.out::println);
The above stream appears to be walking only through non-hidden directories, i.e. directories that do not start with a dot. Unfortunately, you’ve again made mistake #5 and #6. walk() has already produced the whole stream of subdirectories of the current directory. Lazily, though, but logically containing all sub-paths. Now, the filter will correctly filter out paths whose names start with a dot “.”. E.g. .git or .idea will not be part of the resulting stream. But these paths will be: .\.git\refs, or .\.idea\libraries. Not what you intended.
8. Modifying the backing collection of a stream
While you’re iterating a List, you must not modify that same list in the iteration body.
// Of course, we create this list using streams:
List<Integer> list =
IntStream.range(0, 10)
.boxed()
.collect(toCollection(ArrayList::new));
Now, let’s assume that we want to remove each element while consuming it:
list.stream()
// remove(Object), not remove(int)!
.peek(list::remove) // don't do this
.forEach(System.out::println);
9. Forgetting to actually consume the stream
10. Parallel stream deadlock
peek returns stream itself after applying the action passed as consumer object.
list.stream().peek(i->System.out.println(i*i)).collect(Collectors.toList());
http://www.leveluplunch.com/java/examples/stream-intermediate-operations-example/
The Stream.peek is extremely useful during debugging. It allows you to peek into the stream before an action is encountered.
http://eherrera.net/ocpj8-notes/05-java-stream-api.html
Collectos
http://www.concretepage.com/java/jdk-8/java-8-collectors-examples
Double result = list.stream().collect(Collectors.averagingLong(v->v*2));
Double result = list.stream().collect(Collectors.collectingAndThen(Collectors.averagingLong(v->v*2),s-> s*s));
long result= list.stream().collect(Collectors.counting());
Collectors.joining joins the stream elements for a given delimiter, prefix and suffix.
List<String> list = Arrays.asList("A","B","C","D");
String result= list.stream().collect(Collectors.joining(",","(",")"));
list.stream().collect(Collectors.maxBy(new MaxByMinByExample().new IntegerComp()))
.ifPresent(i->System.out.println(i));
int result = list.stream().collect(Collectors.summingInt(i->i));
Map<String,String> map = Stream.of("AA","BB","CC").collect(Collectors.toMap(k->k, v->v+v));
https://forax.github.io/2014-01-29-8684788-Java8_in_8_methods.html
groupBy aggregate
https://dzone.com/articles/java-8-group-collections
This overloaded version of
Using data driven tests in Spock I managed to write almost 40 test cases in no-time, succinctly describing all requirements.
https://www.javacodegeeks.com/2015/03/jdk-8-streams-and-grouping.html
It is possible to group by two different characteristics. This allows for a to be grouped by one characteristic and then have each of those groups sub-grouped by a second characteristic.
http://www.javacodegeeks.com/2015/11/java-8-streams-api-grouping-partitioning-stream.html
Grouping
It’s also possible to count the number of employees in each city, by passing a counting collector to the groupingBy collector. The second collector performs a further reduction operation on all the elements in the stream classified into the same group.
Partitioning
Partitioning is a special kind of grouping, in which the resultant map contains at most two different groups – one for true and one for false. For instance, if you want to find out who your best employees are, you can partition them into those who made more than N sales and those who didn’t, using the partitioningBy collector:
this is also called external iteration because client program is handling the algorithm to iterate over the list.
http://blog.jooq.org/2014/02/14/java-8-friday-goodies-map-enhancements/
http://www.deadcoderising.com/2015-05-19-java-8-replace-traditional-for-loops-with-intstreams/
IntStream.range(1, 3);
// > 1, 2
IntStream.rangeClosed(1, 3);
// > 1, 2, 3
IntStream.iterate(0, i -> i + 2).limit(3);
IntStream.generate(() -> ThreadLocalRandom.current().nextInt(10)).limit(3);
Stream<Color> stream = IntStream.range(1, 5).mapToObj(i -> getColor(i));
Now, if you just want to convert an IntStream
to a Stream<Integer>
, there's a dedicated function for this job called boxed
.
Stream<Integer> stream = IntStream.range(1, 5).boxed();
You'll also find map functions that returns DoubleStream
and LongStream
.
DoubleStream stream = IntStream.range(1, 5).mapToDouble(i -> i);
LongStream stream = IntStream.range(1, 5).mapToLong(i -> i);
Let's start by using anyMatch
to confirm that a certain range contains at least one even number.
IntStream.range(1, 5).anyMatch(i -> i % 2 == 0);
IntStream.range(1, 5)
.filter(i -> i % 2 == 0)
.allMatch(i -> i % 2 == 0);
// > true
IntStream.range(1, 5)
.filter(i -> i % 2 == 0)
.noneMatch(i -> i % 2 != 0);
// > true
http://www.rowkey.me/blog/2017/09/09/java-oneliners/
- 对列表/数组中的每个元素都乘以2
// Range是半开区间 int[] ia = range(1, 10).map(i -> i * 2).toArray(); List<Integer> result = range(1, 10).map(i -> i * 2).boxed().collect(toList());
- 计算集合/数组中的数字之和
range(1, 1000).sum(); range(1, 1000).reduce(0, Integer::sum); Stream.iterate(0, i -> i + 1).limit(1000).reduce(0, Integer::sum); IntStream.iterate(0, i -> i + 1).limit(1000).reduce(0, Integer::sum);
- 验证字符串是否包含集合中的某一字符串
final List<String> keywords = Arrays.asList("brown", "fox", "dog", "pangram"); final String tweet = "The quick brown fox jumps over a lazy dog. #pangram http://www.rinkworks.com/words/pangrams.shtml"; keywords.stream().anyMatch(tweet::contains); keywords.stream().reduce(false, (b, keyword) -> b || tweet.contains(keyword), (l, r) -> l || r);
- 获得集合中最小/最大的数字
int min = Stream.of(14, 35, -7, 46, 98).reduce(Integer::min).get(); min = Stream.of(14, 35, -7, 46, 98).min(Integer::compare).get(); min = Stream.of(14, 35, -7, 46, 98).mapToInt(Integer::new).min(); int max = Stream.of(14, 35, -7, 46, 98).reduce(Integer::max).get(); max = Stream.of(14, 35, -7, 46, 98).max(Integer::compare).get(); max = Stream.of(14, 35, -7, 46, 98).mapToInt(Integer::new).max();
- 并行处理
long result = dataList.parallelStream().mapToInt(line -> processItem(line)).sum();
- 集合上的各种查询(LINQ in Java)
List<Album> albums = Arrays.asList(unapologetic, tailgates, red); //筛选出至少有一个track评级4分以上的专辑,并按照名称排序后打印出来。 albums.stream() .filter(a -> a.tracks.stream().anyMatch(t -> (t.rating >= 4))) .sorted(comparing(album -> album.name)) .forEach(album -> System.out.println(album.name)); //合并所有专辑的track List<Track> allTracks = albums.stream() .flatMap(album -> album.tracks.stream()) .collect(toList()); //根据track的评分对所有track分组 Map<Integer, List<Track>> tracksByRating = allTracks.stream() .collect(groupingBy(Track::getRating));
int[] a = { 1 };
// stream(int[] array)
IntStream s = Arrays.stream(a);
char[] chs = {};
// Object b = Arrays.stream(chs); doesn't work, no override for char[]
// Has IntStream, but no CharStream
https://stackoverflow.com/questions/25441088/group-by-counting-in-java-8-stream-api Map<String, Long> counted = list.stream()
.collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));
https://stackoverflow.com/questions/31554025/want-to-create-a-stream-of-characters-from-char-array-in-javachar[] list = {'a','c','e'};
Stream<Character> cStream = Stream.of(list);
// Stream<Character> cStream = Arrays.stream(list);
The first method does not work (Reason: change cStream to
Stream<char[]>
). The commented line does not also work (Reason: The method stream(T[])
in the type Arrays is not applicable for the arguments (char[]
)).
A short and efficient way to create an
IntStream
from char[]
array is to use java.nio.CharBuffer
:char[] list = {'a','c','e'};
IntStream stream = CharBuffer.wrap(list).chars();
This way you can use an
IntStream
interpreting the int values as characters. If you want a boxed Stream<Character>
(which may be less efficient), useStream<Character> stream = CharBuffer.wrap(list).chars().mapToObj(ch -> (char)ch);
char[] list = {'a','c','e'};
Stream<Character> charStream = new String(list).chars().mapToObj(i->(char)i);
OptionalInt max = IntStream.of(5, 10).max();
Although not as extensive as guava's ranges or rangemaps, IntStream.range returns a sequential IntStream for the range of int elements. IntStream.range first parameter is inclusive while the second is exclusive.
List<Integer> numbers = IntStream.range(1, 3).boxed()
.collect(Collectors.toList());
Map to IntStream
OptionalInt intStream = integers.stream().mapToInt(Integer::parseInt)
.max();
List<Integer> listOfInteger = Arrays.stream(numbers).boxed()
.collect(Collectors.toList());
To obtain a stream from an array, there are plenty of choices. For example, this line of code
Stream stream = Stream.of( "a" , "b" , "c" ); |
produces a stream with the specified elements. The same can also be achieved through:
Stream stream = Arrays.stream( "a" , "b" , "c" ); |
In fact,
Stream.of()
uses Arrays.stream()
to accomplish the taskList<String> list |
= Stream.of( ... ).filter( ... ).collect( Collectors.toList() ); |
String[] array = list.toArray( new String[ list.size() ] ); |
uses
https://javax0.wordpress.com/2017/08/30/noexception-in-stream-operation/toList()
to obtain a list of the filtered input and then turns the list into an array in a second step.Arrays.stream(allowed)
.map(s -> lame(() -> InetAddress.getByName(s)))
.collect(Collectors.toSet());
Collection<InetAddress> allowedAddresses = Arrays.stream(allowed) .map(lame(InetAddress::getByName)) .collect(Collectors.toSet()); |
But Java 8 Collectors class provides a function named collectingAndThenwhich can perform an additional finishing transformation. We can simplify the above code by using collectingAndThen:
List<Shape> immutableBlues = shapes.stream()
.filter(s -> s.getColor() == BLUE)
.collect(collectingAndThen(toList(),
Collections::unmodifiableList))
http://www.baeldung.com/java-8-collectors
List<String> result = givenList.stream()
.collect(collectingAndThen(toList(), ImmutableList::copyOf))
String maxSalaryEmp = employeeList.stream().collect(
Collectors.collectingAndThen(
Collectors.maxBy(Comparator.comparing(Employee::getSalary)),
(Optional<Employee> emp)-> emp.isPresent() ? emp.get().getName() : "none") );
String avgSalary = employeeList.stream().collect( Collectors.collectingAndThen( Collectors.averagingDouble(Employee::getSalary), averageSalary -> new DecimalFormat( "'$'0.00" ).format(averageSalary))); |
http://www.logicbig.com/tutorials/core-java-tutorial/java-util-stream/lazy-evaluation/
Streams are lazy because intermediate operations are not evaluated until terminal operation is invoked.
Each intermediate operation creates a new stream, stores the provided operation/function and return the new stream.
The pipeline accumulates these newly created streams.
The time when terminal operation is called, traversal of streams begins and the associated function is performed one by one.
Parallel streams don't evaluate streams 'one by one' (at terminal point). The operations are rather performed simultaneously, depending on the available cores.
Lazy operations achieve efficiency. It is a way not to work on stale data. Lazy operations might be useful in the situations where input data is consumed gradually rather than having whole complete set of elements beforehand. For example consider the situations where an infinite stream has been created using Stream#generate(Supplier<T>) and the provided Supplier function is gradually receiving data from a remote server. In those kind of the situations server call will only be made at a terminal operation when it's needed.
Also consider a stream on which we have already applied a number of the intermediate operations but haven't applied the terminal operation yet: we can pass around such stream within the application without actually performing any operation on the underlying data, the terminal operation may be called at very different part of the application or at very late in time.
https://bytefish.de/blog/jdk8_files_lines_parallel_stream/
In Java you can basically turn every Stream into a Parallel Stream by calling the
parallel()
method on the Stream. Internally Java 8 uses something called a spliterator
("splittable Iterator") to estimate the optimal chunk size for your data, in order to decompose the problem into sub problems.
The problem with the
Files.lines
Stream implementation in JDK 8 is, that it uses the stream from BufferedReader.lines
. The spliterator
of this stream is derived from an iterator and reports no size. This means the size for the chunks cannot be estimated, and you have no performance gain when processing the stream in parallel.
There is a (solved) ticket for this Issue in the OpenJDK bugtracker:
The bug has been solved for Java 1.9, but the fix also works in Java 1.8 for me. You can find the bugfix in the following commit:
https://stackoverflow.com/questions/44034978/memory-usage-for-a-parallel-stream-from-file-lines
Tracing through the code my guess is the
Spliterator
used by Files.lines()
is Spliterators.IteratorSpliterator
. whose trySplit()
method has this comment: /*
* Split into arrays of arithmetically increasing batch
* sizes. This will only improve parallel performance if
* per-element Consumer actions are more costly than
* transferring them into an array. The use of an
* arithmetic progression in split sizes provides overhead
* vs parallelism bounds that do not particularly favor or
* penalize cases of lightweight vs heavyweight element
* operations, across combinations of #elements vs #cores,
* whether or not either are known. We generate
* O(sqrt(#elements)) splits, allowing O(sqrt(#cores))
* potential speedup.
*/
The code then looks like it splits into batches of multiples of 1024 records (lines). So the first split will read 1024 lines then the next one will read 2048 lines etc on and on. Each split will read larger and larger batch sizes.
If your file is really big, it will eventually hit a max batch size of 33,554,432 which is
1<<25
. Remember that's lines not bytes which will probably cause an out of memory error especially when you start having multiple threads read that many.
That also explains the slow down. Those lines are read ahead of time before the thread can process those lines.
So I would either not use
parallel()
at all or if you must because the computations you are doing are expensive per line, write your own Spliterator that doesn't split like this. Probably just always using a batch of 1024 is fine.LocalDate start = LocalDate.now();
LocalDate end = LocalDate.now().plusMonths(
1
).with(TemporalAdjusters.lastDayOfMonth());
List<LocalDate> dates = Stream.iterate(start, date -> date.plusDays(
1
))
.limit(ChronoUnit.DAYS.between(start, end))
.collect(Collectors.toList());
// Get Min or Max Date
LocalDate maxDate = dates.stream().max( Comparator.comparing( LocalDate::toEpochDay ) ).get();
LocalDate minDate = dates.stream().min( Comparator.comparing( LocalDate::toEpochDay ) ).get();
The method
Comparator.comparing(…)
is intended to create a Comparator
which uses an order based on a property of the objects to compare. When using the lambda expression i -> i
, which is a short writing for (int i) -> { return i; }
here, as a property provider function, the resulting Comparator
will compare the values itself. This works when the objects to compare have a natural order as Integer
has.
So
Stream.of(1,2,4,3,5).max(Comparator.comparing(i -> i))
.ifPresent(maxInt->System.out.println("Maximum number in the set is " + maxInt));
does the same as
Stream.of(1,2,4,3,5).max(Comparator.naturalOrder())
.ifPresent(maxInt->System.out.println("Maximum number in the set is " + maxInt));
though the latter is more efficient as it is implemented as singleton for all types which have a natural order (and implement
Comparable
).
The reason why
max
requires a Comparator
at all, is because you are using the generic class Stream
which might contain arbitrary objects.
This allows, e.g. to use it like
streamOfPoints.max(Comparator.comparing(p->p.x))
to find the point with the largest x
value while Point
itself does not have a natural order. Or do something like streamOfPersons.sorted(Comparator.comparing(Person::getAge))
.
When using the specialized
IntStream
you can use the natural order directly which is likely to be more efficient:IntStream.of(1,2,4,3,5).max()
.ifPresent(maxInt->System.out.println("Maximum number in the set is " + maxInt));
//Find Oldest Person final Comparator<Person> comp = (p1, p2) -> Integer.compare( p1.getAge(), p2.getAge()); Person oldest = personList.stream() .max(comp) .get(); //Find Youngest Person // -This time instead create the Comparator as the argument to the min() method Person youngest = personList.stream() .min((p1, p2) -> Integer.compare(p1.getAge(), p2.getAge())) .get();http://www.technicalkeeda.com/java-8-tutorials/java-8-stream-min-and-max
- System.out.println("Stream.min():- " + Arrays.stream(numbers).min(Integer::compare).get());
- System.out.println("Stream.max():- " + Arrays.stream(numbers).max(Integer::compare).get());
- String min = list.stream().min(Comparator.comparing(String::valueOf)).get();
https://docs.oracle.com/javase/8/docs/api/java/util/stream/Collectors.html#toMap-java.util.function.Function-java.util.function.Function-
If the mapped keys contains duplicates (according to
Object.equals(Object)
), an IllegalStateException
is thrown when the collection operation is performed. If the mapped keys may have duplicates, use toMap(Function, Function, BinaryOperator)
instead.
So you should use
toMap(Function, Function, BinaryOperator)
instead. Just provide merge function, that will determine, which one of duplicates needed to put in map. For example, if you don't care which one, just callMap<String, String> phoneBook = people.stream()
.collect(Collectors.toMap(Person::getName, Person::getAddress, (p1, p2) -> p1));
https://stackoverflow.com/questions/28773685/java-8-tomap-illegalstateexception-duplicate-keyMap<Integer, Integer> map1 = Files.lines(Paths.get(inputFile))
.map(String::trim)
.map(Integer::valueOf)
.collect(Collectors.toMap(x -> x, x -> 1));
If there are duplicates use the following code to get the total number of occurrences in the file for that key.
Map<Integer, Long> map1 = Files.lines(Paths.get(inputFile))
.map(String::trim)
.map(Integer::valueOf)
.collect(Collectors.groupingBy(x -> x, Collectors.counting());
http://www.javabrahman.com/java-8/java-8-mapping-with-streams-map-flatmap-methods-tutorial-with-examples/The
flatMap()
method gives us the ability to flatten a multi-level stream obtained as a result of mapping each element of the input stream to a stream, and then creating a single stream out of this stream of streams.List
is the combination of a map and a flat operation. This means you first apply map function and than flattens the result. Key difference is the function used by map operation returns a Stream of values or list of values rather than single value, that's why we need flattening. When you flat a Stream of Stream, it gets converted into Stream of values.
Before flattening - Stream of List of Integer
After flattening - Stream of Integer
https://www.mkyong.com/java8/java-8-flatmap-example/
temp.flatMap(x -> Arrays.stream(x));
streamArray.flatMapToInt(x -> Arrays.stream(x));
http://www.adam-bien.com/roller/abien/entry/java_8_flatmap_exampleflatMap(l -> l.stream()).
int sum = Arrays.stream(myArray).sum();
String result = Arrays.stream(myArray)
.reduce("", (a,b) -> a + b);
http://stackoverflow.com/questions/31004899/java-8-collectors-tomap-sortedmap
.collect(Collectors.toMap(keyMapper, valueMapper,
(k,v) ->{ throw new RuntimeException(String.format("Duplicate key %s", k));},
TreeMap::new));
Map<Integer, String> sortedMap =
random.entrySet().stream()
.sorted(Map.Entry.comparingByValue())
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue,
(e1, e2) -> e2, LinkedHashMap::new));
stream.flatMap(map -> map.entrySet().stream())
.collect(toMap(
Entry::getKey, Entry::getValue, (v1, v2) -> merge(v1, v2), TreeMap::new));
Map<Integer, String> result2 = list.stream().collect(
Collectors.toMap(x -> x.getId(), x -> x.getName()));
http://stackoverflow.com/questions/20363719/java-8-listv-into-mapk-v
Based on
Collectors
documentation it's as simple as:Map<String, Choice> result =
choices.stream().collect(Collectors.toMap(Choice::getName,
Function.identity()));
Map<String, List<Choice>> result =
choices.stream().collect(Collectors.groupingBy(Choice::getName));
https://www.mkyong.com/java8/java-8-convert-list-to-map/
Map<Integer, String> result2 = list.stream().collect(
Collectors.toMap(x -> x.getId(), x -> x.getName()));
Stream.of("a1", "a2", "a3")
.findFirst()
.ifPresent(System.out::println); // a1
IntStream.range(1, 4)
.forEach(System.out::println);
Arrays.stream(new int[] {1, 2, 3})
.map(n -> 2 * n + 1)
.average()
.ifPresent(System.out::println); // 5.0
http://stackoverflow.com/questions/33016329/cannot-use-java-8-method-with-lambda-arguments-without-specifying-type-arguments
One way to provide enough type information to the compiler is to declare an explicit type of one of the lambda argument. This is in the same spirit as your answer but a little more compact, since you only have to provide the type of the argument, not the whole function.
This looks pretty okay for the one-level map:
level1Map.entrySet().stream()
.flatMap(GdkStreams.flatEntryMapperBuilder(
(Entry<String, Set<String>> entry) -> entry.getKey(),
entry -> entry.getValue().stream()).build());
http://www.leveluplunch.com/java/examples/group-count-repeating-characters-in-string/
Map<String, Long> frequentChars = Arrays.stream(
sentence.toLowerCase().split("")).collect(
Collectors.groupingBy(c -> c, Collectors.counting()));
hello.chars()
.mapToObj(i -> (char)i)
.forEach(System.out::println);
Here I obtain an
IntStream
and map it to an object via the lambda i -> (char)i
, this will automatically box it into a Stream<Character>
, and then we can do what we want, and still use method references as a plus.
Be aware though that you must do
mapToObj
, if you forget and use map
, then nothing will complain, but you will still end up with an IntStream
, and you might be left off wondering why it prints the integer values instead of the strings representing the characters.
The API was designed this way because of not wanting to add
CharStream
, I personally think that the method should return a Stream<Character>
, and the workaround currently is to use mapToObj(i -> (char)i)
on an IntStream
to be able to work properly with them..collect(Collectors.groupingBy(c -> c, Collectors.counting()));
http://stackoverflow.com/questions/23925315/count-int-occurrences-with-java8
Map<Integer, Long> counters =
persons.stream()
.collect(groupingBy(p -> p.getBirthday().getMonthValue(),
counting()));
http://stackoverflow.com/questions/275944/java-how-do-i-count-the-number-of-occurrences-of-a-char-in-a-stringlong java8 = testString.chars().filter(ch -> ch =='.').count();
http://stackoverflow.com/questions/29122394/word-frequency-count-java-8Map<String, Long> collect =
wordsList.stream().collect(groupingBy(Function.identity(), counting()));
Or for Integer values:
Map<String, Integer> collect =
wordsList.stream().collect(groupingBy(Function.identity(), summingInt(e -> 1)));
Debugging
http://stackoverflow.com/questions/24541786/how-to-debug-stream-map-with-lambda-expressions
static int timesTwo(int n) {
Integer result = n * 2;
return result;
}
...
List<Integer> result2 = naturals.stream()
.map(Java8Test::timesTwo)
.collect(Collectors.toList());
...
Thanks to
Marlon Bernardes
answer I noticed that my Eclipse doesn't show what it should and the usage of peek() helped to display results.
Debugger: Stopping at Lambda Expressions
This is becoming pretty similar to Scala. We’re paying the price for shorter, more concise code with more complex debugging, and longer synthetic call stacks.
The reason is that while javac has been extended to support Lambda functions, the JVM still remains oblivious to them. This has been a design decision by the Java folks in order to to keep the JVM operating at a lower-level, and without introducing new elements into its specification.
http://blog.takipi.com/benchmark-how-java-8-lambdas-and-streams-can-make-your-code-5-times-slower/max = integers.parallelStream().reduce(Integer::max)
integers.stream().reduce(Integer.MIN_VALUE, (a, b) -> Integer.max(a, b));
http://blog.codefx.org/java/stream-performance/
http://blog.codefx.org/java/stream-performance/
1. Performance – you will lose on it
list.forEach(System.out::println);
for
(Integer i : list)
for
(
int
j =
0
; j < i; j++)
System.out.println(i * j);
// "Modern"
list.forEach(i -> {
IntStream.range(
0
, i).forEach(j -> {
System.out.println(i * j);
});
});
Filter
https://www.mkyong.com/java8/java-8-streams-filter-examples/
List<String> lines = Arrays.asList("spring", "node", "mkyong");
List<String> result = lines.stream() //convert list to stream
.filter(line -> !"mkyong". equals (line)) //filters the line, equals to "mkyong"
.collect(Collectors.toList()); //collect the output and convert streams to a List
result.forEach(System.out::println); //output : spring node
http://stackoverflow.com/questions/33635717/in-java-streams-is-peek-really-only-for-debugging
Don't use the API in an unintended way, even if it accomplishes your immediate goal. That approach may break in the future, and it is also unclear to future maintainers.
http://stackoverflow.com/questions/22532051/is-it-possible-to-debug-lambdas-in-java-8http://stackoverflow.com/questions/22601036/stream-from-two-dimensional-array-in-java
Stream from two dimensional array in java
Assuming you want to process array of array sequentially in row-major approach, this should work:
int[][] arr = { {1, 2, 3}, {4, 5, 6}, {7, 8, 9} };
IntStream stream = Arrays.stream(arr).flatMapToInt(x -> Arrays.stream(x));
First it invokes the
http://www.mkyong.com/java8/java-8-filter-a-map-examples/Arrays.stream(T[])
method, where T
is inferred as int[]
, to get a Stream<int[]>
, and then Stream#flatMapToInt()
method maps each int[]
element to an IntStream
using Arrays.stream(int[])
method. result = HOSTING.entrySet().stream()
.filter(map -> "aws.amazon.com".equals(map.getValue()))
.map(map -> map.getValue())
.collect(Collectors.joining());
Map<Integer, String> collect = A_MAP_EXAMPLE.entrySet().stream()
.filter(map -> map.getKey() == 2)
.collect(Collectors.toMap(p -> p.getKey(), p -> p.getValue()));
http://www.programcreek.com/2014/01/convert-stream-to-array-in-java-8/
To convert a Stream to an array, there is an overloaded version of toArray() method for Stream objects. The
toArray(IntFunction<A[]> generator)
method returns an array containing the elements of this stream, using the provided generator function to allocate the returned array.
We can also use the method reference format which does exactly the same thing.
http://codurance.com/2015/05/04/side-effects-and-java-8-streams/https://blog.jooq.org/2014/06/13/java-8-friday-10-subtle-mistakes-when-using-the-streams-api/
1. Accidentally reusing streams
2. Accidentally creating “infinite” streams
IntStream.iterate(0, i -> i + 1)
.forEach(System.out::println);
3. Accidentally creating “subtle” infinite streams
4. Accidentally creating “subtle” parallel infinite streams
IntStream.iterate(0, i -> ( i + 1 ) % 2)
.parallel()
.distinct()
.limit(10)
.forEach(System.out::println);
5. Mixing up the order of operations
IntStream.iterate(0, i -> i + 1)
.limit(10) // LIMIT
.skip(5) // OFFSET
.forEach(System.out::println);
7. Walking the file system with filters
Files.walk(Paths.get("."))
.filter(p -> !p.toFile().getName().startsWith("."))
.forEach(System.out::println);
The above stream appears to be walking only through non-hidden directories, i.e. directories that do not start with a dot. Unfortunately, you’ve again made mistake #5 and #6. walk() has already produced the whole stream of subdirectories of the current directory. Lazily, though, but logically containing all sub-paths. Now, the filter will correctly filter out paths whose names start with a dot “.”. E.g. .git or .idea will not be part of the resulting stream. But these paths will be: .\.git\refs, or .\.idea\libraries. Not what you intended.
8. Modifying the backing collection of a stream
While you’re iterating a List, you must not modify that same list in the iteration body.
// Of course, we create this list using streams:
List<Integer> list =
IntStream.range(0, 10)
.boxed()
.collect(toCollection(ArrayList::new));
Now, let’s assume that we want to remove each element while consuming it:
list.stream()
// remove(Object), not remove(int)!
.peek(list::remove) // don't do this
.forEach(System.out::println);
9. Forgetting to actually consume the stream
10. Parallel stream deadlock
peek returns stream itself after applying the action passed as consumer object.
list.stream().peek(i->System.out.println(i*i)).collect(Collectors.toList());
http://www.leveluplunch.com/java/examples/stream-intermediate-operations-example/
The Stream.peek is extremely useful during debugging. It allows you to peek into the stream before an action is encountered.
http://eherrera.net/ocpj8-notes/05-java-stream-api.html
Peek is an intermediate operation that returns a stream consisting of the elements of the original stream, and performing the provided action on each element.
According to the API, This method exists mainly to support debugging, where you want to see the elements as they flow past a certain point in a pipeline:
Stream.of("the", "good", "bad", "ugly")
.filter(e -> e.length() > 3)
.peek(e -> System.out.println("Filtered value: " + e))
.map(String::toUpperCase)
.peek(e -> System.out.println("Mapped value: " + e))
.collect(Collectors.toList());
long uniqueWords = java.nio.file.Files
.lines(Paths.get(file.toURI()), Charset.defaultCharset())
.flatMap(line -> Arrays.stream(line.split(" ."))).distinct()
.count();
Collectos
http://www.concretepage.com/java/jdk-8/java-8-collectors-examples
Double result = list.stream().collect(Collectors.averagingLong(v->v*2));
Double result = list.stream().collect(Collectors.collectingAndThen(Collectors.averagingLong(v->v*2),s-> s*s));
long result= list.stream().collect(Collectors.counting());
Collectors.joining joins the stream elements for a given delimiter, prefix and suffix.
List<String> list = Arrays.asList("A","B","C","D");
String result= list.stream().collect(Collectors.joining(",","(",")"));
list.stream().collect(Collectors.maxBy(new MaxByMinByExample().new IntegerComp()))
.ifPresent(i->System.out.println(i));
int result = list.stream().collect(Collectors.summingInt(i->i));
Map<String,String> map = Stream.of("AA","BB","CC").collect(Collectors.toMap(k->k, v->v+v));
https://forax.github.io/2014-01-29-8684788-Java8_in_8_methods.html
In fact, you don't need to use
computeIfAbsent
if you want to do a groupBy
, you can transform the collection or the array to a Stream
of objects, do transformation on each one and then collect all the results in a List
or a Map
using a set predefinedCollectors
.Map<String, List<Person>> byNameMap =
people.stream().collect(Collectors.groupingBy(Person::getName));
Path file = Paths.get("file.txt");
Map<String, Long> histoMap =
Files.lines(file)
.flatMap(line -> Arrays.stream(line.split(" ")))
.collect(Collectors.groupingBy(Function.identity(),
Collectors.counting()));
public static Map<String, Integer> countOldschool(String input) {
Map<String, Integer> wordcount = new HashMap<>();
Matcher matcher = Pattern.compile("\\w+").matcher(input);
while (matcher.find()) {
String word = matcher.group().toLowerCase();
wordcount.put(word, wordcount.getOrDefault(word, 0) + 1);
}
return wordcount;
}
http://stackoverflow.com/questions/29122394/word-frequency-count-java-8Map<String, Long> collect =
wordsList.stream().collect(groupingBy(Function.identity(), counting());
Or for Integer value:
Map<String, Integer> collect =
wordsList.stream().collect(groupingBy(Function.identity(), summingInt(e -> 1));
Map<String, Integer> counts = list.parallelStream().
collect(Collectors.toConcurrentMap(
w -> w, w -> 1, Integer::sum));
public static Map<String, Integer> wordCount(Stream<String> stream) {
return stream
.flatMap(s -> Stream.of(s.split("\\s+")))
.collect(Collectors.toMap(s -> s, s -> 1, Integer::sum));
}
public static <T, K> Collector<T, ?, Map<K, List<T>>>
groupingBy(Function<? super T, ? extends K> classifier) {
return groupingBy(classifier, toList());
}
public static <T, K, A, D>
Collector<T, ?, Map<K, D>> groupingBy(Function<? super T, ? extends K> classifier,
Collector<? super T, A, D> downstream) {
return groupingBy(classifier, HashMap::new, downstream);
}
Collector<T, ?, M> groupingBy(Function<? super T, ? extends K> classifier,
Supplier<M> mapFactory,
Collector<? super T, A, D> downstream)
http://www.leveluplunch.com/java/examples/java-util-stream-groupingBy-example/ Map<String, List<StudentClass>> groupByTeachers = studentClasses
.stream().collect(
Collectors.groupingBy(StudentClass::getTeacher));
Map<Double, Long> groupByLevel = studentClasses.stream().collect(
Collectors.groupingBy(StudentClass::getLevel,
Collectors.counting()));
Stream<Person> people = Stream.of(new Person("Paul", 24), new Person("Mark", 30), new Person("Will", 28)); Map<Integer, List<String>> peopleByAge = people .collect(groupingBy(p -> p.age, mapping((Person p) -> p.name, toList())));
final Map<Sex, Long> bySex = people
.stream()
.collect(
groupingBy(Person::getSex, HashMap::new, counting()));
groupingBy()
takes three parameters. First one is the key (classifier) function, as previously. Second argument creates a new map, we'll see shortly why it's useful. counting()
is a nested collector that takes all people with same sex and combines them together - in our case simply counting them as they arrive. Being able to choose map implementation is useful e.g. when building age histogram. We would like to know how many people we have at given age - but age values should be sorted:final
TreeMap<Integer, Long> byAge = people
.stream()
.collect(
groupingBy(Person::getAge, TreeMap::
new
, counting()));
Sampling, batching and sliding window
IterableLike.sliding()
method in Scala allows to view a collection through a sliding fixed-size window. This window starts at the beginning and in each iteration moves by given number of items. Such functionality, missing in Java 8, allows several useful operators like computing moving average, splitting big collection into batches (compare with Lists.partition()
in Guava) or sampling every n-th element. We will implement collector for Java 8 providing similar behaviour. Using data driven tests in Spock I managed to write almost 40 test cases in no-time, succinctly describing all requirements.
https://www.javacodegeeks.com/2015/03/jdk-8-streams-and-grouping.html
It is possible to group by two different characteristics. This allows for a to be grouped by one characteristic and then have each of those groups sub-grouped by a second characteristic.
Grouping
It’s also possible to count the number of employees in each city, by passing a counting collector to the groupingBy collector. The second collector performs a further reduction operation on all the elements in the stream classified into the same group.
Another example is calculating the average number of sales in each city, which can be done using the collector in conjuction with the collector:
Partitioning is a special kind of grouping, in which the resultant map contains at most two different groups – one for true and one for false. For instance, if you want to find out who your best employees are, you can partition them into those who made more than N sales and those who didn’t, using the partitioningBy collector:
this is also called external iteration because client program is handling the algorithm to iterate over the list.
A collection is an in-memory data structure to hold values and before we start using collection, all the values should have been populated. Whereas a Stream is a data structure that is computed on-demand.
Stream doesn’t store data, it operates on the source data structure (collection and array) and produce pipelined data that we can use and perform specific operations. Such as we can create a stream from the list and filter it based on a condition.
Since we can use primitive data types such as int, long in the collections using auto-boxing and these operations could take a lot of time, there are specific classes for these – IntStream
, LongStream
and DoubleStream
.http://blog.jooq.org/2014/02/14/java-8-friday-goodies-map-enhancements/
compute() methods
Example:
http://www.leveluplunch.com/java/examples/filter-map-by-entries/
Optional<Integer> intOptional = numbers.reduce((i,j) -> {return i*j;});
System.out.println("Number of elements in stream="+numbers1.count()); //5
numbers.forEach(i -> System.out.print(i + ",")); // 1,2,3,4,5,
System.out.println("Stream contains 4? " + numbers.anyMatch(i -> i == 4));
System.out.println("Stream contains all elements less than 10? " + numbers.allMatch(i -> i < 10));
System.out.println("Stream doesn't contain 10? " + numbers.noneMatch(i -> i == 10));
final Optional<String> firstNameWithD = names4.filter(i -> i.startsWith("D")).findFirst();
Stream groupingBy
http://www.leveluplunch.com/java/examples/java-util-stream-groupingBy-example/
SELECT column_name, count(column_name) FROM table GROUP BY column_name;
Map<String, List<StudentClass>> groupByTeachers = studentClasses.stream().collect(Collectors.groupingBy(StudentClass::getTeacher));
Stream groupingBy
SELECT CLASS_LEVEL, count(CLASS_LEVEL) FROM STUDENT_CLASS GROUP BY CLASS_LEVEL;
final Map<Double, Long> groupByLevel = studentClasses.stream().collect(Collectors.groupingBy(StudentClass::getLevel, Collectors.counting()));
http://winterbe.com/posts/2014/07/31/java8-stream-tutorial-examples/
Sometimes it's useful to transform a regular object stream to a primitive stream or vice versa.
FlatMap transforms each element of the stream into a stream of other objects. So each object will be transformed into zero, one or multiple other objects backed by streams. The contents of those streams will then be placed into the returned stream of the
all parallel stream operations share the same JVM-wide common
https://thecannycoder.wordpress.com/2014/08/15/collectors-part-2-provided-collectors-and-a-java-8-streams-demonstration/
Note we’re streaming Integer rather than int so we need to pass a ToIntFunction function for the sum, average and summarizing collectors. A ToIntFunction applies a function to a type and returns an int. Given the stream has an Object shape we need to help the compiler and indicate the parameter to the lambda is really an Integer. Auto-unboxing will do the rest.
The second is a groupingBy collector. This allows us to group together elements that have the same classification into a map. To classify we pass a classification function which takes an element and returns the type we are using for the keys of the map. GroupingBy has three versions: the first just takes the classification function, the second allows us to specify the the collection type for values with duplicate keys (default is a generic list). The third is the same as the second, but also has another parameter (2nd one) which allows us to specify the type of map (as opposed to just a generic one). We pass a constructor using the :: notation and the constructor is denoted by the ‘new’ function.
https://thecannycoder.wordpress.com/2014/08/10/collectors-part-1-reductions-and-short-circuiting-operations%e2%80%8f/
a binary function which takes two values and returns a single one
we may also need an initial value (termed the identity)
What if both the stream is empty and there was no identity value? To solve this problem, the version of the API without an identity value returns an appropriate Optional.
https://thecannycoder.wordpress.com/2014/07/04/generators/
Instead of using IntStream’s iterate function, we can use generate instead. IntStream’s generate function takes an instance of an IntSupplier. IntSupplier has a getAsInt() function which returns the next int in the sequence which is very much like our next() function.
https://thecannycoder.wordpress.com/2014/07/12/finite-sequence-generators/
Lambda expressions bind to interfaces with one single method left to implement. If there were more we couldn’t do this binding.
https://thecannycoder.wordpress.com/2014/06/29/ranges-and-looping-with-intstream/
https://www.tobyhobson.co.uk/java-8-parallel-streams-fork-join-pool/
myList.parallelStream.map(obj -> longRunningOperation())
Behind the scenes the JVM uses a common fork join pool which is shared across all parallel streams. By default it uses a fork join pool with one thread per processor.
Arranges to asynchronously execute this task in the pool the current task is running in, if applicable, or using the ForkJoinPool.commonPool() if not inForkJoinPool()
http://colobu.com/2016/03/02/Java-Stream/
Often, we fetch a value from a map, make some calculations on it and put it back into the map. This can be verbose and hard to get right if concurrency is involved. With Java 8, we can pass a
BiFunction
to the new compute()
,computeIfAbsent()
, or computeIfPresent()
methods and have the Map
implementation handle the semantics of replacing a value.map.forEach((k, v) ->
System.out.println(k +
"="
+ v));
Example:
http://www.leveluplunch.com/java/examples/filter-map-by-entries/
Map<Integer, String> monthsWithLengthFour =
MONTHS.entrySet()
.stream()
.filter(p -> p.getValue().length() == 4)
.collect(Collectors.toMap(p -> p.getKey(), p -> p.getValue()));
http://www.programcreek.com/2014/01/java-8-counter/String[] arr = {"program", "creek", "program", "creek", "java", "web", "program"}; Stream<String> stream = Stream.of(arr).parallel(); Map<String, Long> counter = stream.collect(Collectors.groupingBy(String::toString, Collectors.counting())); System.out.println(counter.get("creek"));http://stackoverflow.com/questions/29122394/word-frequency-count-java-8
List<String> list = Arrays.asList(
"hello", "bye", "ciao", "bye", "ciao");
Map<String, Integer> counts = list.parallelStream().
collect(Collectors.toConcurrentMap(
w -> w, w -> 1, Integer::sum));
System.out.println(counts);
http://stackoverflow.com/questions/29625529/hashmap-with-streams-in-java-8-streams-to-collect-value-of-mapOptional<List> o = id1.entrySet()
.stream()
.filter( e -> e.getKey() == 1)
.map(Map.Entry::getValue)
.findFirst();
In the general case, if the filter may match multiple Lists, you can collect them to a List of Lists :
List<List> list = id1.entrySet()
.stream()
.filter(.. some predicate...)
.map(Map.Entry::getValue)
.collect(Collectors.toList());
http://www.journaldev.com/2774/java-8-stream-api-example-tutorialOptional<Integer> intOptional = numbers.reduce((i,j) -> {return i*j;});
System.out.println("Number of elements in stream="+numbers1.count()); //5
numbers.forEach(i -> System.out.print(i + ",")); // 1,2,3,4,5,
System.out.println("Stream contains 4? " + numbers.anyMatch(i -> i == 4));
System.out.println("Stream contains all elements less than 10? " + numbers.allMatch(i -> i < 10));
System.out.println("Stream doesn't contain 10? " + numbers.noneMatch(i -> i == 10));
final Optional<String> firstNameWithD = names4.filter(i -> i.startsWith("D")).findFirst();
Stream groupingBy
http://www.leveluplunch.com/java/examples/java-util-stream-groupingBy-example/
SELECT column_name, count(column_name) FROM table GROUP BY column_name;
Map<String, List<StudentClass>> groupByTeachers = studentClasses.stream().collect(Collectors.groupingBy(StudentClass::getTeacher));
Stream groupingBy
SELECT CLASS_LEVEL, count(CLASS_LEVEL) FROM STUDENT_CLASS GROUP BY CLASS_LEVEL;
final Map<Double, Long> groupByLevel = studentClasses.stream().collect(Collectors.groupingBy(StudentClass::getLevel, Collectors.counting()));
http://winterbe.com/posts/2014/07/31/java8-stream-tutorial-examples/
Arrays.asList("a1", "a2", "a3")
.stream()
.findFirst()
.ifPresent(System.out::println); // a1
myList
.stream()
.filter(s -> s.startsWith("c"))
.map(String::toUpperCase)
.sorted()
.forEach(System.out::println);
And primitive streams support the additional terminal aggregate operations sum()
and average()
:Sometimes it's useful to transform a regular object stream to a primitive stream or vice versa.
Stream.of("a1", "a2", "a3")
.map(s -> s.substring(1))
.mapToInt(Integer::parseInt)
.max()
.ifPresent(System.out::println);
Primitive streams can be transformed to object streams via mapToObj()
:Stream.of(1.0, 2.0, 3.0)
.mapToInt(Double::intValue)
.mapToObj(i -> "a" + i)
.forEach(System.out::println);
An important characteristic of intermediate operations is laziness.Stream.of("d2", "a2", "b1", "b3", "c")
.filter(s -> {
System.out.println("filter: " + s);
return true;
})
.forEach(s -> System.out.println("forEach: " + s));
Why order mattersStream.of("d2", "a2", "b1", "b3", "c")
.filter(s -> {
System.out.println("filter: " + s);
return s.startsWith("a");
})
.map(s -> {
System.out.println("map: " + s);
return s.toUpperCase();
})
.forEach(s -> System.out.println("forEach: " + s));
Sorting is a special kind of intermediate operation. It's a so called stateful operation since in order to sort a collection of elements you have to maintain state during ordering.
Stream.of("d2", "a2", "b1", "b3", "c")
.filter(s -> {
System.out.println("filter: " + s);
return s.startsWith("a");
})
.sorted((s1, s2) -> {
System.out.printf("sort: %s; %s\n", s1, s2);
return s1.compareTo(s2);
})
.map(s -> {
System.out.println("map: " + s);
return s.toUpperCase();
})
.forEach(s -> System.out.println("forEach: " + s));
Java 8 streams cannot be reused. As soon as you call any terminal operation the stream is closed:
To overcome this limitation we have to to create a new stream chain for every terminal operation we want to execute, e.g. we could create a stream supplier to construct a new stream with all intermediate operations already set up:
Supplier<Stream<String>> streamSupplier =
() -> Stream.of("d2", "a2", "b1", "b3", "c")
.filter(s -> s.startsWith("a"));
streamSupplier.get().anyMatch(s -> true); // ok
streamSupplier.get().noneMatch(s -> true); // ok
Each call to
get()
constructs a new stream on which we are save to call the desired terminal operation.
Collect is an extremely useful terminal operation to transform the elements of the stream into a different kind of result, e.g. a
List
, Set
or Map
. Collect accepts a Collector
which consists of four different operations: a supplier, an accumulator, a combiner and a finisher. This sounds super complicated at first, but the good part is Java 8 supports various built-in collectors via the Collectors
class.Map<Integer, List<Person>> personsByAge = persons
.stream()
.collect(Collectors.groupingBy(p -> p.age));
personsByAge
.forEach((age, p) -> System.out.format("age %s: %s\n", age, p));
Map<Integer, List<Person>> personsByAge = persons
.stream()
.collect(Collectors.groupingBy(p -> p.age));
personsByAge
.forEach((age, p) -> System.out.format("age %s: %s\n", age, p));
Double averageAge = persons
.stream()
.collect(Collectors.averagingInt(p -> p.age));
IntSummaryStatistics ageSummary =
persons
.stream()
.collect(Collectors.summarizingInt(p -> p.age));
The join collector accepts a delimiter as well as an optional prefix and suffix.
String phrase = persons
.stream()
.filter(p -> p.age >= 18)
.map(p -> p.name)
.collect(Collectors.joining(" and ", "In Germany ", " are of legal age."));
In order to transform the stream elements into a map, we have to specify how both the keys and the values should be mapped. Keep in mind that the mapped keys must be unique, otherwise anIllegalStateException
is thrown. You can optionally pass a merge function as an additional parameter to bypass the exception:
Map<Integer, String> map = persons
.stream()
.collect(Collectors.toMap(
p -> p.age,
p -> p.name,
(name1, name2) -> name1 + ";" + name2));
FlatMapFlatMap transforms each element of the stream into a stream of other objects. So each object will be transformed into zero, one or multiple other objects backed by streams. The contents of those streams will then be placed into the returned stream of the
flatMap
operation.foos.stream()
.flatMap(f -> f.bars.stream())
.forEach(b -> System.out.println(b.name));
IntStream.range(1, 4)
.mapToObj(i -> new Foo("Foo" + i))
.peek(f -> IntStream.range(1, 4)
.mapToObj(i -> new Bar("Bar" + i + " <- " f.name))
.forEach(f.bars::add))
.flatMap(f -> f.bars.stream())
.forEach(b -> System.out.println(b.name));
.stream()
.reduce((p1, p2) -> p1.age > p2.age ? p1 : p2)
.ifPresent(System.out::println); // Pamela
The second reduce
method accepts both an identity value and a BinaryOperator
accumulator. Person result =
persons
.stream()
.reduce(new Person("", 0), (p1, p2) -> {
p1.age += p2.age;
p1.name += p2.name;
return p1;
});
The third reduce
method accepts three parameters: an identity value, a BiFunction
accumulator and a combiner function of type BinaryOperator
Integer ageSum = persons
.stream()
.reduce(0, (sum, p) -> sum += p.age, (sum1, sum2) -> sum1 + sum2);
Executing this stream in parallel results in an entirely different execution behavior. Now the combiner is actually called. Since the accumulator is called in parallel, the combiner is needed to sum up the separate accumulated values.
Parallel streams use a common
ForkJoinPool
available via the staticForkJoinPool.commonPool()
method. sort
on a parallel stream uses the new Java 8 method Arrays.parallelSort()
under the hood. As stated in Javadoc this method decides on the length of the array if sorting will be performed sequentially or in parallel:But keep in mind that some parallel stream operations likeIf the length of the specified array is less than the minimum granularity, then it is sorted using the appropriate Arrays.sort method.
reduce
and collect
need additional computations (combine operations) which isn't needed when executed sequentially.all parallel stream operations share the same JVM-wide common
ForkJoinPool
. So you probably want to avoid implementing slow blocking stream operations since that could potentially slow down other parts of your application which rely heavily on parallel streams.https://thecannycoder.wordpress.com/2014/08/15/collectors-part-2-provided-collectors-and-a-java-8-streams-demonstration/
Integer[] numbersArray =
new
Integer[] {
1
,
2
,
3
,
4
,
5
};
System.out.println(Arrays.stream(numbersArray)
.collect(Collectors.counting()));
System.out.println(Arrays.stream(numbersArray)
.collect(
Collectors.summingInt((Integer x) -> x)));
System.out.println(Arrays.stream(numbersArray)
.collect(
Collectors.averagingInt((Integer x) -> x)));
System.out.println(Arrays.stream(numbersArray)
.collect(
Collectors.maxBy(Integer::compare)).get());
System.out.println(Arrays.stream(numbersArray)
.collect(
Collectors.minBy(Integer::compare)).get());
System.out.println(Arrays.stream(numbersArray)
.collect(
Collectors.summarizingInt((Integer x) -> x)));
What I did when I explored was make simple examples like those I’m presenting:
- Try the simplest version with the least parameters. That will usually be the easiest to understand.
- Once you get that working look at the implementation. Often the simpler one will be passing its own ‘default’ parameters to a more complicated version giving a clue to what that is expecting.
- Try the more complicated one, but substitute the ‘default’ parameter with something else. See what happens. Does it compile and do what’s expected, if not why not?
- Try stepping through the library code and see how the code is using the parameters.
- The first two parameters map our value onto a key and a value respectively. For one of these (often the value) we don’t want to change anything and so passing Function.identity() (or the lambda v -> v) will keep the value the same.
- By default toMap uses a throwingMerger() which throws an IllegalStateException if two keys clash. We can see this in action if we force the keys to a single value. If we specify a third parameter we can specify a BiFunction (two parameters in, one out) to deal with the clash instead. If the result of this function is null, the latest is kept.
- If we specify a fourth parameter to toMap we can specify a specific type of map.
- The documentation doesn’t state what type of collection is returned for toList(), toSet() and the 2 and 3 parameter versions of toMap(). The idea is that functional programming provides a rich set of features for a few collections rather than lots of collections. We therefore shouldn’t make any assumptions and where it matters use toCollection or the 4 parameter version of toMap to be sure, or convert later.
- There are also concurrent versions of toMap called toConcurrentMap which can give better performance in parallel streams when we don’t care about the order.
The second is a groupingBy collector. This allows us to group together elements that have the same classification into a map. To classify we pass a classification function which takes an element and returns the type we are using for the keys of the map. GroupingBy has three versions: the first just takes the classification function, the second allows us to specify the the collection type for values with duplicate keys (default is a generic list). The third is the same as the second, but also has another parameter (2nd one) which allows us to specify the type of map (as opposed to just a generic one). We pass a constructor using the :: notation and the constructor is denoted by the ‘new’ function.
The third set is a partitioningBy collector. This is like groupingBy, but instead of passing a function to specify the key, we pass a predicate which determines the key (true or false) for each value. In the single parameter version, values that share the same key are organised into a generic list, where as in the two parameter version we can specify the collection type for the values.
public
class
Tuple2<T1, T2>
{
public
T1 t1;
public
T2 t2;}
public class IntegerRange extends Tuple2<Integer, Integer>
{
public IntegerRange(Integer start, Integer end)
{
super(start, end);
}
public Integer getStart()
{
return t1;
}
public Integer getEnd()
{
return t2;
}
public final Predicate<Integer> inRange =
i -> i >= t1 && i < t2;
}
https://thecannycoder.wordpress.com/2014/08/10/collectors-part-1-reductions-and-short-circuiting-operations%e2%80%8f/
To do reduction we need one or two thing
- The reduction operation is also called ‘fold left’ since if we drew a tree it would be leaning left.
System.out.println(IntStream.rangeClosed(
1
, n)
.reduce((x, y) -> x * y).getAsInt());
IntStream myStream = IntStream.iterate(
1
,
i -> ((
int
) Math.pow(Math.sqrt(i) +
1
,
2
)));
myStream.limit(
10
).forEach(System.out::println);
https://thecannycoder.wordpress.com/2014/07/12/finite-sequence-generators/
Lambda expressions bind to interfaces with one single method left to implement. If there were more we couldn’t do this binding.
Stream<Integer> stream = StreamSupport.stream(
new
DieThrowIterable().spliterator(),
false
);
IntStream.rangeClosed(
1
,
10
)
.forEach(i -> IntStream.rangeClosed(
1
,
10
)
.forEach(
j -> System.out.println(i +
" * "
+ j +
" = "
+ i * j)));
myList.parallelStream.map(obj -> longRunningOperation())
Behind the scenes the JVM uses a common fork join pool which is shared across all parallel streams. By default it uses a fork join pool with one thread per processor.
Arranges to asynchronously execute this task in the pool the current task is running in, if applicable, or using the ForkJoinPool.commonPool() if not inForkJoinPool()
ForkJoinPool forkJoinPool = new ForkJoinPool(3);
forkJoinPool.submit(() -> {
firstRange.parallelStream().forEach((number) -> {
try {
Thread.sleep(5);
} catch (InterruptedException e) { }
});
});
Because we created our own thread pools we can avoid the shared thread pool and we can even allocate more threads than processor cores if we so wish:http://colobu.com/2016/03/02/Java-Stream/
- 不存储数据。流是基于数据源的对象,它本身不存储数据元素,而是通过管道将数据源的元素传递给操作。
- 函数式编程。流的操作不会修改数据源,例如
filter
不会将数据源中的数据删除。 - 延迟操作。流的很多操作如filter,map等中间操作是延迟执行的,只有到终点操作才会将操作顺序执行。
- 可以解绑。对于无限数量的流,有些操作是可以在有限的时间完成的,比如
limit(n)
或findFirst()
,这些操作可是实现"短路"(Short-circuiting),访问到有限的元素后就可以返回。 - 纯消费。流的元素只能访问一次,类似Iterator,操作没有回头路,如果你想从头重新访问流的元素,对不起,你得重新生成一个新的流。
流的操作是以管道的方式串起来的。流管道包含一个数据源,接着包含零到N个中间操作,最后以一个终点操作结束。
Collection.stream()
为集合创建串行流而Collection.parallelStream()
为集合创建并行流。IntStream.range(int, int)
创建的是串行流。通过parallel()
方法可以将串行流转换成并行流,sequential()
方法将流转换成串行流。
除非方法的Javadoc中指明了方法在并行执行的时候结果是不确定(比如findAny、forEach),否则串行和并行执行的结果应该是一样的。
流可以从非线程安全的集合中创建,当流的管道执行的时候,非concurrent数据源不应该被改变。下面的代码会抛出java.util.ConcurrentModificationException异常:
List<String> l = new ArrayList(Arrays.asList("one", "two"));
Stream<String> sl = l.stream();
sl.forEach(s -> l.add("three"));
在设置中间操作的时候,可以更改数据源,只有在执行终点操作的时候,才有可能出现并发问题(抛出异常,或者不期望的结果),比如下面的代码不会抛出异常:
List<String> l = new ArrayList(Arrays.asList("one", "two"));
Stream<String> sl = l.stream();
l.add("three");
sl.forEach(System.out::println);
对于concurrent数据源,不会有这样的问题,比如下面的代码很正常:
List<String> l = new CopyOnWriteArrayList<>(Arrays.asList("one", "two"));
Stream<String> sl = l.stream();
sl.forEach(s -> l.add("three"));
虽然我们上面例子是在终点操作中对非并发数据源进行修改,但是非并发数据源也可能在其它线程中修改,同样会有并发问题。
某些流的返回的元素是有确定顺序的,我们称之为 encounter order。这个顺序是流提供它的元素的顺序,比如数组的encounter order是它的元素的排序顺序,List是它的迭代顺序(iteration order),对于HashSet,它本身就没有encounter order。
一个流是否是encounter order主要依赖数据源和它的中间操作,比如数据源List和Array上创建的流是有序的(ordered),但是在HashSet创建的流不是有序的。
sorted()方法可以将流转换成有序的,unordered可以将流转换成无序的。
除此之外,一个操作可能会影响流的有序,比如map方法,它会用不同的值甚至类型替换流中的元素,所以输入元素的有序性已经变得没有意义了,但是对于filter方法来说,它只是丢弃掉一些值而已,输入元素的有序性还是保障的。
对于串行流,流有序与否不会影响其性能,只是会影响确定性(determinism),无序流在多次执行的时候结果可能是不一样的。
对于并行流,去掉有序这个约束可能会提供性能,比如distinct、groupingBy这些聚合操作。
peek方法方法会使用一个Consumer消费流中的元素,但是返回的流还是包含原来的流中的元素。
String[] arr = new String[]{"a","b","c","d"};
Arrays.stream(arr)
.peek(System.out::println) //a,b,c,d
.count();
List<String> l = new ArrayList(Arrays.asList("one", "two"));
Stream<String> sl = l.stream();
sl.forEach(s -> l.add("three"));
在设置中间操作的时候,可以更改数据源,只有在执行终点操作的时候,才有可能出现并发问题(抛出异常,或者不期望的结果),比如下面的代码不会抛出异常:
List<String> l = new ArrayList(Arrays.asList("one", "two"));
Stream<String> sl = l.stream();
l.add("three");
sl.forEach(System.out::println);
对于concurrent数据源,不会有这样的问题,比如下面的代码很正常:
List<String> l = new CopyOnWriteArrayList<>(Arrays.asList("one", "two"));
Stream<String> sl = l.stream();
sl.forEach(s -> l.add("three"));
虽然我们上面例子是在终点操作中对非并发数据源进行修改,但是非并发数据源也可能在其它线程中修改,同样会有并发问题。
某些流的返回的元素是有确定顺序的,我们称之为 encounter order。这个顺序是流提供它的元素的顺序,比如数组的encounter order是它的元素的排序顺序,List是它的迭代顺序(iteration order),对于HashSet,它本身就没有encounter order。
一个流是否是encounter order主要依赖数据源和它的中间操作,比如数据源List和Array上创建的流是有序的(ordered),但是在HashSet创建的流不是有序的。
sorted()方法可以将流转换成有序的,unordered可以将流转换成无序的。
除此之外,一个操作可能会影响流的有序,比如map方法,它会用不同的值甚至类型替换流中的元素,所以输入元素的有序性已经变得没有意义了,但是对于filter方法来说,它只是丢弃掉一些值而已,输入元素的有序性还是保障的。
对于串行流,流有序与否不会影响其性能,只是会影响确定性(determinism),无序流在多次执行的时候结果可能是不一样的。
对于并行流,去掉有序这个约束可能会提供性能,比如distinct、groupingBy这些聚合操作。
peek方法方法会使用一个Consumer消费流中的元素,但是返回的流还是包含原来的流中的元素。
String[] arr = new String[]{"a","b","c","d"};
Arrays.stream(arr)
.peek(System.out::println) //a,b,c,d
.count();