1

Am writting a Linear Search implementation. I have written the code by using Java 7 and 8 as well.

Below is my code:

int[] arr = new int[] { 12, 34, 94, 8, 37, 10 };
System.out.println("Enter the element to search: ");
Scanner scan = new Scanner(System.in);
int element = scan.nextInt();
boolean found = false;

// Linear search implementation using Java 7 features
for (int i = 0; i < arr.length; i++) {
    if (element == arr[i]) {
        int n = element;
        System.out.println("Element : "+n+" has been found at the index : "+i);
        found = true;
        break;
    }
}

// Linear search implementation using Java 8 features
IntStream.range(0, arr.length)
    .forEach(i -> {
        if (element == arr[i]) {
            int n = element;
            System.out.println("Element : " + n + " has been found at the index : " + i);
            found = true;
            break;
        }
    });

if (!found) {
    System.out.println("Element : "+ element + " not found.");
}

Following piece of code is working properly:

for (int i = 0; i < arr.length; i++) {
    if (element == arr[i]) {
        int n = element;
        System.out.println("Element : " + n + " has been found at the index : " + i);
        found = true;
        break;
    }
}

But, below code with Java 8 features throwing error for the fields inside forEach loop

IntStream.range(0, arr.length)
    .forEach(i -> {
        if (element == arr[i]) {
            int n = element;
            System.out.println("Element : " + n + " has been found at the index : " + i);
            found = true;
            break;
        }
    });

An error occurs:

Local variable found defined in an enclosing scope must be final or effectively final

break cannot be used outside of a loop or a switch

How can I access boolean found inside forEach block in Java 8 features?

Roland
  • 22,259
  • 4
  • 57
  • 84
Karthikeyan
  • 1,927
  • 6
  • 44
  • 109

3 Answers3

5

Actually the message says it already. Note that when working with streams you usually try to avoid .forEach (or at least you do not just transform your for-loops into .forEach).

What you probably rather wanted is something like the following:

IntStream.range(0, arr.length)
        .filter(i -> element == arr[i]) // .equals( ??
        .findFirst()
        .ifPresent(i -> System.out.println("Element : "+element+" has been found at the index : "+i));

If you have to deal also with the case were none is found, use the following:

OptionalInt result = IntStream.range(0, arr.length)
        .filter(i -> element == arr[i]) // .equals( ??
        .findFirst();
if (result.isPresent()) {
  System.out.printf("Element : %s has been found at the index : %d%n", element, result.getAsInt());
} else {
  // none found
  System.out.printf("Element %s not found%n", element);
}

Newer Java versions support ifPresentOrElse which might be useful here, e.g. (code tested by MC Emperor, as I have no Java >8 here ;-)):

...
.findFirst()
.ifPresentOrElse(i -> System.out.printf("Element : %s has been found at the index : %d%n", element, i),
                () -> System.out.printf("Element %s not found%n", element));
Roland
  • 22,259
  • 4
  • 57
  • 84
1

You should not access the boolean found but return that value from the stream.

int element;
OptionalInt valueOptional = Arrays.stream(arr)
                                 .filter(current -> current == element)
                                 .findFirst();

Then, check weather the optional is present or not.

valueOptional.isPresent();
1

There are multiple ways to do that, depending on your exact requirements:

Indexes

You could of course create an IntStream containing all indexes of the given array:

IntStream.range(0, arr.length)

You can then check if the element at the given position is equal to element:

    .filter(i -> arr[i] == element)

We can abort walking if we find any array element equal to element:

    .findAny()

This returns an OptionalInt, which contains the first index of the found element. OptionalInt is designed to contain either a value, or not. This is better than using a sentinel value (like, for example, -1).

You can then perform a certain action if the value is found:

    .ifPresent(i -> System.out.println("Element " + element + " found at index " + i))

Stream over the elements without using index

If you don't care about the actual index, you can also stream the array directly. The Arrays class within the java.util package provides a method to stream an int[], returning an IntStream, which is a stream designed to stream a source of ints.

Arrays.stream(arr)
    .filter(t -> t == element)
    .findAny()
    .ifPresent(t -> ...);

More about streams

The elements of a stream are lazily consumed. That means that the stream terminates if enough elements are consumed to get to the final result. For example, Arrays.stream(arr).findAny() only touches the first element of arr. You could say that findAny() is similar to if (found) { break; } in its effect.


Note that you're actually using a lambda expression. That is a new language construct introduced in Java 8. forEach is nothing more than a method accepting a Consumer, which is in turn a functional interface. You can only access variables which are final or effectively final. The forEach method is different from a for loop, so you cannot actually use break.

MC Emperor
  • 22,334
  • 15
  • 80
  • 130