Post summary: This post explains Java 8 Stream API with very basic code examples.
In Java 8 features – Lambda expressions, Interface changes, Stream API, DateTime API post I have briefly described most interesting Java 8 features. In the current post, I will give special attention to Stream API. This post is with very basic code examples to explain the theory described in Java 8 features – Stream API explained post. Code examples here can be found in GitHub java-samples/java8 repository.
Example for filter, map, distinct, sorted, peek and collect
I will cover all those operations in one example. Code below takes a list of strings and converts it to stream by stream() method. For debug purposes peek() is used in the beginning and at the end of stream operations. It only prints to the console elements from the stream. Filtering of the elements is done by filter() method. Lambda expression is used as a predicate. This lambda expression is a method call to verify current element is a number: element-> NumberUtils.isNumber(element). Since it is a single method call it is substituted with method reference: NumberUtils::isNumber. All elements that are evaluated to false are removed from further processing. It is good practice to use filtering at the beginning of stream pipeline so stream elements are reduced. Next operation is converting String values in the stream to Long values. This is done with map() method again with method reference. Duplicated elements are removed by calling distinct(). Stream elements are sorted by element’s natural order, in the current example, they are Long values. In the end, the stream is materialized into a List by using collect(Collectors.toList()) method. If this code has to be written without streams it would have looked as shown in “no stream code” tab. Note that using stream code is much more readable. Actually, in the beginning, it is not that easy to think in a stream-oriented way, but once you get used to it, you will never want to see the non-streams code.
code
public static List<Long> toLongList(List<String> stringList) {
return stringList.stream()
.peek(element -> System.out.println("Before: " + element))
.filter(NumberUtils::isNumber)
.map(Long::valueOf)
.distinct()
.sorted()
.peek(element -> System.out.println("After: " + element))
.collect(Collectors.toList());
}
unit test
@Test
public void test_toLongList() {
List<String> stringList = Arrays
.asList(null, "", "aaa", "345", "123", "234", "123");
List<Long> result = BasicStreamExamples.toLongList(stringList);
assertEquals(3, result.size());
assertEquals(123L, (long) result.get(0));
assertEquals(234L, (long) result.get(1));
assertEquals(345L, (long) result.get(2));
}
console output
Before: null
Before:
Before: aaa
Before: 345
Before: 123
Before: 234
Before: 123
After: 123
After: 234
After: 345
no stream code
public static List<Long> toLongListWithoutStream(List<String> stringList) {
List<Long> result = new ArrayList<>();
for (String value : stringList) {
System.out.println("Before: " + value);
if (NumberUtils.isNumber(value)) {
Long longValue = Long.valueOf(value);
if (!result.contains(longValue)) {
result.add(longValue);
System.out.println("After: " + value);
}
}
}
Collections.sort(result);
return result;
}
Example for toArray
This example is similar to the example above, instead of collecting as a list here stream elements are returned in the array.
toArray code
public static Long[] toLongArray(String[] stringArray) {
return Arrays.stream(stringArray)
.filter(NumberUtils::isNumber)
.map(Long::valueOf)
.toArray(Long[]::new);
}
unit test
@Test
public void test_toLongArray() {
String[] stringArray = new String[] {null, "", "aaa", "123", "234"};
Long[] result = BasicStreamExamples.toLongArray(stringArray);
assertEquals(2, result.length);
assertEquals(123L, (long) result[0]);
assertEquals(234L, (long) result[1]);
}
Example for flatMap
This function is pretty complex and hard to understand. In the current example, there is a map with String for key and List for value. The example below merges all list values in one result list. Note that Map interface does not have stream() method. Instead, first entrySet() is invoked which returns Set and then invoke its stream() method. Once stream is created flatMap() is called and result of Function argument should be stream: map -> map.getValue().stream(). This resultant stream is a merge of all list values streams, which is then collected to a List.
flatMap code
public static List<String> flapMap(Map<String, List<String>> mapToProcess) {
return mapToProcess.entrySet()
.stream()
.flatMap(map -> map.getValue().stream())
.collect(Collectors.toList());
}
unit test
@Test
public void test_flapMap() {
Map<String, List<String>> map = new HashMap<>();
map.put("1", Arrays.asList("a", "b"));
map.put("2", Arrays.asList("C", "D"));
List<String> expectedResult = Arrays.asList("a", "b", "C", "D");
List<String> result = BasicStreamExamples.flapMap(map);
assertEquals(expectedResult, result);
}
Examples of limit and skip
limit code
public static List<String> limitValues(List<String> stringList, long limit) {
return stringList.stream()
.limit(limit)
.collect(Collectors.toList());
}
limit unit test
@Test
public void test_limitValues() {
List<String> stringList = Arrays.asList("a", "b", "c", "d");
List<String> result = BasicStreamExamples.limitValues(stringList, 2);
assertEquals(2, result.size());
assertEquals("a", result.get(0));
assertEquals("b", result.get(1));
}
skip code
public static List<String> skipValues(List<String> stringList, long skip) {
return stringList.stream()
.skip(skip)
.collect(Collectors.toList());
}
skip unit test
@Test
public void test_skipValues() {
List<String> stringList = Arrays.asList("a", "b", "c", "d");
List<String> result = BasicStreamExamples.skipValues(stringList, 2);
assertEquals(2, result.size());
assertEquals("c", result.get(0));
assertEquals("d", result.get(1));
}
Example for forEach
forEach code
public static void printEachElement(List<String> stringList) {
stringList.stream()
.forEach(element -> System.out.println("Element: " + element));
}
unit test
@Test
public void test_printEachElement() {
List<String> stringList = Arrays.asList("a", "b", "c", "d");
BasicStreamExamples.printEachElement(stringList);
}
console output
Element: a
Element: b
Element: c
Element: d
Examples of min and max
min code
public static Optional<Integer> getMin(List<Integer> stringList) {
return stringList.stream()
.min(Long::compare);
}
min unit test
@Test
public void test_getMin() {
List<Integer> integerList = Arrays.asList(234, 123, 345);
Optional<Integer> result = BasicStreamExamples.getMin(integerList);
assertEquals(123, (int) result.get());
}
max code
public static Optional<Integer> getMax(List<Integer> integers) {
return integers.stream()
.max(Long::compare);
}
max unit test
@Test
public void test_getMax() {
List<Integer> integerList = Arrays.asList(234, 123, 345);
Optional<Integer> result = BasicStreamExamples.getMax(integerList);
assertEquals(345, (int) result.get());
}
Example for reduce
This also is a bit complex method. The method that is given below sums all elements in the provided stream.
reduce code
public static Optional<Integer> sumByReduce(List<Integer> integers) {
return integers.stream()
.reduce((x, y) -> x + y);
}
unit test
@Test
public void test_sumByReduce() {
List<Integer> integerList = Arrays.asList(100, 200, 300);
Optional<Integer> result = BasicStreamExamples.sumByReduce(integerList);
assertEquals(600, (int) result.get());
}
Example for count
count code
public static long count(List<Integer> integers) {
return integers.stream()
.count();
}
unit test
@Test
public void test_count() {
List<Integer> integerList = Arrays.asList(234, 123, 345);
long result = BasicStreamExamples.count(integerList);
assertEquals(3, result);
}
Example for anyMatch, allMatch, and noneMatch
anyMatch code
public static boolean isOddElementPresent(List<Integer> integers) {
return integers.stream()
.anyMatch(element -> element % 2 != 0);
}
allMatch code
public static boolean areAllElementsOdd(List<Integer> integers) {
return integers.stream()
.allMatch(element -> element % 2 != 0);
}
noneMatch code
public static boolean areAllElementsEven(List<Integer> integers) {
return integers.stream()
.noneMatch(element -> element % 2 != 0);
}
unit test 1
@Test
public void test_anyMatch_allMatch_noneMatch_allEven() {
List<Integer> integerList = Arrays.asList(234, 124, 346, 124);
assertFalse(BasicStreamExamples.isOddElementPresent(integerList));
assertFalse(BasicStreamExamples.areAllElementsOdd(integerList));
assertTrue(BasicStreamExamples.areAllElementsEven(integerList));
}
unit test 2
@Test
public void test_anyMatch_allMatch_noneMatch_evenAndOdd() {
List<Integer> integerList = Arrays.asList(234, 123, 345, 123);
assertTrue(BasicStreamExamples.isOddElementPresent(integerList));
assertFalse(BasicStreamExamples.areAllElementsOdd(integerList));
assertFalse(BasicStreamExamples.areAllElementsEven(integerList));
}
unit test 3
@Test
public void test_anyMatch_allMatch_noneMatch_allOdd() {
List<Integer> integerList = Arrays.asList(233, 123, 345, 123);
assertTrue(BasicStreamExamples.isOddElementPresent(integerList));
assertTrue(BasicStreamExamples.areAllElementsOdd(integerList));
assertFalse(BasicStreamExamples.areAllElementsEven(integerList));
}
Examples for findFirst
In case of List stream has an order and it will return always 234 as result.
findFirst code for List
public static Optional<Integer> getFirstElementList(List<Integer> integers) {
return integers.stream()
.findFirst();
}
findFirst unit test for List
@Test
public void test_getFirstElementList() {
List<Integer> integerList = Arrays.asList(234, 123, 345, 123);
Optional<Integer> result = BasicStreamExamples
.getFirstElementList(integerList);
assertEquals(Integer.valueOf(234), result.get());
}
Since Set has no natural order then there is no guarantee which element is to be returned by findFirst(). On my machine, with my JVM it is 345, but on another machine, with other JVM it might be a different value, so this test most likely will fail for someone else.
findFirst code for Set
public static Optional<Integer> getFirstElementSet(Set<Integer> integers) {
return integers.stream()
.findFirst();
}
findFirst unit test for Set
@Test
public void test_getFirstElementSet() {
Set<Integer> integerSet = new HashSet<>();
integerSet.add(234);
integerSet.add(123);
integerSet.add(345);
integerSet.add(123);
Optional<Integer> result = BasicStreamExamples
.getFirstElementSet(integerSet);
assertEquals(Integer.valueOf(345), result.get());
}
Examples for findAny
There is no guarantee which element is to be returned by findAny(). On my machine, with my JVM it is 234, but on another machine, with other JVM it might be a different value, so this test most likely will fail for someone else.
findAny code
public static Optional<Integer> getAnyElement(List<Integer> integers) {
return integers.stream()
.findAny();
}
findAny unit test
@Test
public void test_getGetAnyElement() {
List<Integer> integerList = Arrays.asList(234, 123, 345, 123);
Optional<Integer> result = BasicStreamExamples
.getAnyElement(integerList);
assertEquals(Integer.valueOf(234), result.get());
}
Conclusion
These basic code examples give an idea how Java 8 Stream API operations work. More advanced examples are shown in Java 8 features – Stream API advanced examples post.