Swimming Upstream
A Beginner’s Guide to Understanding Stream Notation in Java
Streams are another way to do something to every member of a set (similar to forEach). You can process only as much of the stream as you want to to accomplish the goal. It’s like a forEach with an automatic break in the code. Streams can dramatically shorten the code that we have to create.
I spent some time diagramming the code from Friday’s stream examples to better show what each part of a stream statement is doing, and what equivalent logic looks like in loops. Since I know some of the other bootcampers had questions about the syntax, I though I’d post them on the blog to make them easier to find.
Example of .forEach()
The .forEach() method of Stream class does exactly what you’d expect it to do - it itereates through all the elements in a stream (just like the for each loop we’ve already learned about).
Stream statement diagram:
OriginalObject | Streaming | Logic loop | Lambda(ƛ) parameter | Lambda(ƛ) expression |
---|---|---|---|---|
numbers | .stream() | .forEach | (x -> | System.out.println(x)); |
Stream Code:
List <Integer> numbers = Arrays.asList(2,3,4,5);
numbers.stream().forEach(x -> System.out.println(x));
Output:
2
3
4
5
Works Like This Regular For Each Loop
List <Integer> numbers = Arrays.asList(2,3,4,5);
for (int x: numbers){
System.out.println(x*x);
}
Example of .map() & .collect()
The .collect() method of Stream class gathers elements of any Stream into a Collection. If you want to return a value, you will need a collector to collect the results.
New variable | OriginalObject | Streaming | Map Function | ƛ parameter & expression | Get all the results | Puts result in list |
---|---|---|---|---|---|---|
List newList = | numbers | .stream() | .map( | x -> x*x) | .collect | (Collectors.toList()); |
Stream Code
List <Integer> numbers = Arrays.asList(2,3,4,5);
List newList = numbers.stream().map(x -> x*x).collect(Collectors.toList());
Output
None, but now newList = [4,9,16,25]
Works Like This Regular For Each Loop
List <Integer> numbers = Arrays.asList(2,3,4,5);
List newList = new ArrayList();
for(int x : numbers){
newList.add(x*x);
}
Example of .map() and .forEach()
The .map() method is used when we want to convert a stream of x
to stream of f(x)
(aka y
).
OriginalObject | Streaming | Map Function | ƛ parameter & expression (input ->output) | Logic loop | print out the result of x*x |
numbers | .stream() | .map( | x -> x*x) | .forEach | (y -> System.out.println(y);) |
Stream Code
List <Integer> numbers = Arrays.asList(2,3,4,5);
numbers.stream().map(x -> x*x).forEach(y -> System.out.println(y));
Output
4
9
16
25
Works Like This Regular For Each Loop
List <Integer> numbers2 = Arrays.asList(2,3,4,5);
List newList = new ArrayList();
for(int x : numbers2){
System.out.println(x*x);
}
Example of .filter() & .collect()
The filter method is used to select elements as per the Predicate passed as argument.
OriginalObject | Streaming | filter | ƛ parameter & expression | Collecting |
names | .stream() | .filter( | s -> s.startsWith(“S”)) | .collect(Collectors.toList()); |
Stream Code
List <String> names = Arrays.asList("Reflection","Collection","Stream");
List result = names.stream().filter(s->s.startsWith("S")).collect(Collectors.toList());
Output
Output: none, but result = [Stream]
Works Like This Regular For Each Loop
List <String> names = Arrays.asList("Reflection","Collection","Stream");
List result = new ArrayList();
for (String name: names){
if (name.startsWith("S")){
result.add(name);
}
}
Example of .filter() & .reduce()
The reduce method is used to reduce the elements of a stream to a single value. The reduce method takes a binary operator as a parameter.
OriginalObject | Streaming | filter | ƛ parameter & expression | .reduce |
int even = | .stream() | .filter | (x-> x%2 ==0) | .reduce(0,*ans, i)->ans+1); |
Stream Code
List number = Arrays.asList(2,3,4,5);
int even = number.stream().filter(x -> x%2 == 0).reduce(0, (ans, i) -> ans + i));
Output
Output: none, but even = 6
This is becuase the function first filters out the even numbers (2 & 4), then adds them.
Works Like This Regular For Each Loop
List numbers = Arrays.asList(2,3,4,5);
List result = new ArrayList();
for (Integer x: numbers){
if (x%2==0){
result.add(x);
}
}
Example of .sort() & .collect()
The .sort() method returns a stream consisting of the elements of the original stream, sorted according to natural order (numerical, alphabetical, etc.). For ordered streams, the sort method is works well, but for unordered streams, things can get wierd.
Original Object | Streaming | Using .sort() method | List being sorted | Collecting ordered results in a list |
names | .stream() | .sort( | myNames) | .collect(Collectors.toList()); |
Stream Code
Stream Code:
String[] family = {"Rachel", "Brian", "Molly", "Lucy", "Alice", "Elliott"};
List<String> names = Arrays.asList(family);
List<String> sortedNames = names.stream().sorted().collect(Collectors.toList());
Output
Output: none, sortedNames = [Alice, Brian, Elliott, Lucy, Molly, Rachel]
Works Like This Without Streams
String[] family = {"Rachel", "Brian", "Molly", "Lucy", "Alice", "Elliott"};
List<String> names = Arrays.asList(family);
names.sort(String.CASE_INSENSITIVE_ORDER);