Resolving Common Issues Faced With Using Streams in Java

Posted By : Amit Kumar | 30-Dec-2018

This blog describes some approaches to handle the most common issues faced with Streams or lambda expressions on which they depend on, for example - the usage of local variables defined outside lambda expressions and others

1)Accessing a variable defined outside a lambda expression

One of the common issues faced with lambda expressions is, one where we are trying to use the value of another local variable inside the current function. In this example, we try to calculate the sum of the members of an Integer List.

 

List<Integer> numList = Arrays.asList(3, 1, 4, 1, 5, 9);
int sum = 0;

//Compilation Error - Local variable sum defined an enclosing scope must be final  or effectively final
numList.forEach(n -> sum += n);           

//The above compilation error can be resolved by the following 
sum = numList.stream()                    
            .mapToInt(Integer::valueOf)
            .sum();

 

Prior to Java 8, the sum variable must be final, but Java 8 adds another term effectively final, releasing the use of the final keyword.


2)Generating Random Numbers using Streams

We can use the java.util.Random class to create Random Number streams in a specific range. For example
If you want a sequential stream of random numbers, however, Java 8 added several methods to the Random class that returns them. The methods are ints, longs, and doubles

 

IntStream    ints()
LongStream   longs()
DoubleStream doubles()

Random r = new Random();
r.ints(5)                           
 .sorted()
 .forEach(System.out::println);

r.doubles(5, 0, 0.5)                
 .sorted()
 .forEach(System.out::println);

List<Long> longs = r.longs(5)
                    .boxed()        
                    .collect(Collectors.toList());
System.out.println(longs);

 

The latter two examples deal with the minor annoyance that occurs when you want to create a collection of primitives. You can’t invoke collect(Collectors.toList()) on a collection of primitives.We can either use the boxed method to convert the long values to instances of Long, or you can use the three-argument version of collect and specify the Supplier, accumulator, and combiner yourself, as shown.

It’s worth noting that SecureRandom is a subclass of Random. It provides a cryptographically strong random number generator. All the same methods (ints, longs, doubles, and their overloads) also work on SecureRandom, just with a different generator.

3) Using java.util.Objects methods for better filtering of streams
The Objects class provides some of the following functions

 

//Checks for “deep” equality , useful when comparing two arrays.
static boolean deepEquals(Object a, Object b)

//a null safe equality check.
static boolean equals(Object a, Object b)

//Returns T if not null and throws a NullPointerException (NPE) otherwise.
static <T> T requireNotNull(T obj)

//Returns true if the provided reference is null and false otherwise.
static boolean isNull(Object obj)

//Returns true if the provided reference is not null and false otherwise.
static boolean nonNull(Object obj)

//Filter out null elements
List<String> strings = Arrays.asList("list", "of", "strings", null);

List<String> nonNullStrings = strings.stream()
    .filter(Objects::nonNull)       
    .collect(Collectors.toList());

public <T> List<T> getNonNullElements(List<T> list) {
    return list.stream()
            .filter(Objects::nonNull)
            .collect(Collectors.toList());
}

public void testNonNulls() throws Exception {
    List<String> strings =
        Arrays.asList("list", "of", "strings");
    assertTrue(Objects.deepEquals(strings, nonNullStrings);
}

 

4) Catching uthe nchecked exception in lambdas
Like any Java code, a lambda expression the can throw a runtime exception without declaring it or wrapping the code in a try/catch block. The exception is then propagated to the caller.

Consider, for instance, a method that dithe vides all elements of a collection by a constant value, as shown.
A lambda expression with unchecked exception

 

public List<Integer> div(List<Integer> values, Integer factor) {
    return values.stream()
        .map(n -> n / factor) 
        .collect(Collectors.toList());
}

 

Integer division will throw an ArithmeticException (an unchecked exception) if the denominator is zero.This will be propagated to the caller, as shown in Example

 

public List<Integer> div(List<Integer> values, Integer factor) {
    return values.stream()
        .map( n -> {
            try {
                return n / factor;
            } catch (ArithmeticException e) {
                e.printStackTrace();
            }
        })
        .collect(Collectors.toList());
}

 

The client code invokes the div method, and if the divisor is zero, the lambda expression throws an ArithmeticException. The client can add a try/catch block inside the map method in order to handle the exception, but that leads to some seriously ugly code.

Lambda expression with try/catch

 

public List<Integer> div(List<Integer> values, Integer factor) {
    return values.stream()
        .map( n -> {
            try {
                return n / factor;
            } catch (ArithmeticException e) {
                e.printStackTrace();
            }
        })
        .collect(Collectors.toList());
}

 

It’s generally a good idea to keep stream processing code as simple as possible, with the goal of writing one line per intermediate operation. In this case, you can simplify the code by extracting the function inside map into a method, and the stream processing could be done by calling it, as 

//Extracting a lambda into a method

 

//Extracting a lambda into a method
private Integer divide(Integer value, Integer factor) {
    try {
        return value / factor;
    } catch (ArithmeticException e) { 
        e.printStackTrace();
    }
}

public List<Integer> divUsingMethod(List<Integer> values, Integer factor) {
    return values.stream()
        .map(n -> divide(n, factor))  
        .collect(Collectors.toList());
}

 

As an aside, if the extracted method had not needed the factor value, the argument to map could have been simplified to a method reference.

The technique of extracting the lambda to a separate method has benefits as well. You can write tests for the extracted method (using reflection if the method is private), set break points in it, or any other mechanism normally associated with methods.

About Author

Author Image
Amit Kumar

Amit is an seasoned Backend Developer with expertise in Java, particularly the Spring framework as well as Javascript and related frameworks such as Node.js and Express. He possesses a solid understanding of blockchain fundamentals and has hands-on experience integrating major blockchains like Ethereum and Bitcoin into both centralized and decentralized applications. He is proficient in working with relational and non-relational databases, enabling him to handle various data storage requirements effectively. He has successfully contributed to a range of projects, including Belfrics, a cryptocurrency exchange platform, Daxxcoin, an Ethereum Proof-Of-Work fork, and Wethio blockchain, an Ethereum-based Proof-Of-Stake fork. His strong technical skills and practical experience make him a valuable asset, particularly in the blockchain domain. Amit's ability to integrate blockchain technologies into applications showcases his versatility and innovation.

Request for Proposal

Name is required

Comment is required

Sending message..