Java 8 features - Stream API basic 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.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());
}
@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));
}
Before: null
Before:
Before: aaa
Before: 345
Before: 123
Before: 234
Before: 123
After: 123
After: 234
After: 345
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.public static Long[] toLongArray(String[] stringArray) {
return Arrays.stream(stringArray)
.filter(NumberUtils::isNumber)
.map(Long::valueOf)
.toArray(Long[]::new);
}
@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.public static List<String> flapMap(Map<String, List<String>> mapToProcess) {
return mapToProcess.entrySet()
.stream()
.flatMap(map -> map.getValue().stream())
.collect(Collectors.toList());
}
@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
public static List<String> limitValues(List<String> stringList, long limit) {
return stringList.stream()
.limit(limit)
.collect(Collectors.toList());
}
@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));
}
public static List<String> skipValues(List<String> stringList, long skip) {
return stringList.stream()
.skip(skip)
.collect(Collectors.toList());
}
@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
public static void printEachElement(List<String> stringList) {
stringList.stream()
.forEach(element -> System.out.println("Element: " + element));
}
@Test
public void test_printEachElement() {
List<String> stringList = Arrays.asList("a", "b", "c", "d");
BasicStreamExamples.printEachElement(stringList);
}
Element: a
Element: b
Element: c
Element: d
Examples of min and max
public static Optional<Integer> getMin(List<Integer> stringList) {
return stringList.stream()
.min(Long::compare);
}
@Test
public void test_getMin() {
List<Integer> integerList = Arrays.asList(234, 123, 345);
Optional<Integer> result = BasicStreamExamples.getMin(integerList);
assertEquals(123, (int) result.get());
}
public static Optional<Integer> getMax(List<Integer> integers) {
return integers.stream()
.max(Long::compare);
}
@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.public static Optional<Integer> sumByReduce(List<Integer> integers) {
return integers.stream()
.reduce((x, y) -> x + y);
}
@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
public static long count(List<Integer> integers) {
return integers.stream()
.count();
}
@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
public static boolean isOddElementPresent(List<Integer> integers) {
return integers.stream()
.anyMatch(element -> element % 2 != 0);
}
public static boolean areAllElementsOdd(List<Integer> integers) {
return integers.stream()
.allMatch(element -> element % 2 != 0);
}
public static boolean areAllElementsEven(List<Integer> integers) {
return integers.stream()
.noneMatch(element -> element % 2 != 0);
}
@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));
}
@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));
}
@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.public static Optional<Integer> getFirstElementList(List<Integer> integers) {
return integers.stream()
.findFirst();
}
@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.
public static Optional<Integer> getFirstElementSet(Set<Integer> integers) {
return integers.stream()
.findFirst();
}
@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.public static Optional<Integer> getAnyElement(List<Integer> integers) {
return integers.stream()
.findAny();
}
@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());
}