Showing posts with label stream. Show all posts
Showing posts with label stream. Show all posts

Thursday, 15 June 2023

NullPointerException in Stream

Just a small blurb. I recently helped a colleague who got a NullPointerException whilst dealing with streams.

It looked very innocuous at first.

  @Test(expectedExceptions = NullPointerException.class)
  public void testStream() {
    Optional<String> first = Stream.of(new Person(null, "mrBear"), new Person("George", "Boole"),
            new Person("Ada", "Lovelace"),
            new Person("Tim", "Berners-Lee"), new Person("James", "Gosling"), new Person("Linus", "Torvalds"))
        .map(Person::firstName).findFirst();
  }

Well, it turns out that, if you have a list with null-values, and you try a findFirst() on the stream, and the first value is null, Java will attempt to wrap the null-value into an Optional, which is not allowed.

You get a fancy stacktrace, that shows just this:

java.lang.NullPointerException
	at java.base/java.util.Objects.requireNonNull(Objects.java:208)
	at java.base/java.util.Optional.of(Optional.java:113)
	at java.base/java.util.stream.FindOps$FindSink$OfRef.get(FindOps.java:194)
	at java.base/java.util.stream.FindOps$FindSink$OfRef.get(FindOps.java:191)
	at java.base/java.util.stream.FindOps$FindOp.evaluateSequential(FindOps.java:150)
	at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
	at java.base/java.util.stream.ReferencePipeline.findFirst(ReferencePipeline.java:647)
	at com.mrbear.streams.NullPointerExceptionTest.testStream(NullPointerExceptionTest.java:18)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:568)
	at org.testng.internal.MethodInvocationHelper.invokeMethod(MethodInvocationHelper.java:124)
	at org.testng.internal.Invoker.invokeMethod(Invoker.java:571)
	at org.testng.internal.Invoker.invokeTestMethod(Invoker.java:707)
	at org.testng.internal.Invoker.invokeTestMethods(Invoker.java:979)
	at org.testng.internal.TestMethodWorker.invokeTestMethods(TestMethodWorker.java:125)
	at org.testng.internal.TestMethodWorker.run(TestMethodWorker.java:109)
	at org.testng.TestRunner.privateRun(TestRunner.java:648)
	at org.testng.TestRunner.run(TestRunner.java:505)
	at org.testng.SuiteRunner.runTest(SuiteRunner.java:455)
	at org.testng.SuiteRunner.runSequentially(SuiteRunner.java:450)
	at org.testng.SuiteRunner.privateRun(SuiteRunner.java:415)
	at org.testng.SuiteRunner.run(SuiteRunner.java:364)
	at org.testng.SuiteRunnerWorker.runSuite(SuiteRunnerWorker.java:52)
	at org.testng.SuiteRunnerWorker.run(SuiteRunnerWorker.java:84)
	at org.testng.TestNG.runSuitesSequentially(TestNG.java:1187)
	at org.testng.TestNG.runSuitesLocally(TestNG.java:1116)
	at org.testng.TestNG.runSuites(TestNG.java:1028)
	at org.testng.TestNG.run(TestNG.java:996)
	at com.intellij.rt.testng.IDEARemoteTestNG.run(IDEARemoteTestNG.java:66)
	at com.intellij.rt.testng.RemoteTestNGStarter.main(RemoteTestNGStarter.java:105)

Just something to keep in mind.

Thursday, 10 March 2022

For-loops and Streams

So, I'm almost always trying to use Streams in Java.

There are only a handful of times when Streams are not a perfect fit. And most of the times, this is when there is more than one list, and you need to traverse them in sync.

So, we have different ways of doing the same thing. Let's count 'em down.

We'll use the following test setup, using the new Java Money and Currency reference implementation1 2.

Use an ordinary for-loop

This was way back when we didn't have anything better.

And let's be honest, sometimes this is all you need and it is still surprisingly readable.

Use a for-each loop

This is one of my, let us say, less perfect attempts.

It's nice that I've used the for-each construct, but too bad I've hacked an additional index to it to do what I want.

Streams!

So, my colleague at work provided me with this solution, to get away from the whole for-loop (at least, superficially, when you look deep deep down into it, it's just a for loop written as a stream.)

I has to use a reduce in the example, because one of the disadvantages here, is that it's not allowed to reassign variables inside a lambda. Variables should be effectively final.

This disadvantage is not present in for-loops.

But I've also seen some truly horrendous hacks where people started using things like AtomicInteger as a "wrapper" workaround, so they could manipulate the inside of the wrapper inside the lambda.

References

[1] Baeldung - Java Money and the Currency API
https://www.baeldung.com/java-money-and-currency
[2] JSR 354: Money and Currency API
https://jcp.org/en/jsr/detail?id=354
Medium - Experienced Developers, Use These Quirks to Create Better Java Lambdas
https://medium.com/javarevisited/experienced-developers-use-these-quirks-to-create-better-java-lambdas-4ae656148274

Sunday, 21 April 2019

Refactoring into Streams

So, as often happens, I encountered the following code at work:

It's basically a zipped file, containing other zipped files, containing supermarket sales item data. This little bit of code just decides if a file should be parsed.

SonarLint quite rightly complained "Refactor this method to reduce its Cognitive Complexity".

And I quite quickly came up with the thought that it's really a list, where we can stop evaluating the list after the first match.

So, it becomes something like:

But, of course we have streams now:

It looks better, no?

It's a trivial example, but I like it.

Addendum

A colleague mentioned that I should do a bit of this differently. Move some more stuff into the enum.

The result becomes something like this:

Moving knowledge into the Enum, seems to be a better fit here.

Friday, 10 August 2018

Can I use a Stream multiple times?

Just a quick question, that I answered using a small unit test.

Stream<String> stream = Arrays.asList("John""Paul""Ringo""George").stream();
assertThat(stream.filter(x -> x.equals("Matthias")).collect(Collectors.toList())).hasSize(0);
assertThat(stream.count()).isEqualTo(4);

Does this work?

Answer in my next blog.

Thursday, 6 July 2017

Lambdas, New IO, and parsing textfiles in a hurry.

Okay, so I needed to do some parsing of a file containing URLs (which I "wget"-ted) and moving the retrieved files to proper locations.

I decided to write a quick Java program to do this instead of messing around with scripting languages or a Linux bash shell.

It worked very well, and I am rather pleased with the result and Java 8.

It contains the following "new/newer/not-very-old" stuff:
  • a lambda
  • a stream (of Strings)
  • a method reference (used as a lambda)
  • the java.nio.file package (New IO)
One small note: lambdas implement an interface. In this case the forEach requires a lambda that implements the Consumer interface. The Consumer interface does not specify an IOException. Therefore, I am required to catch it here and rethrow it unchecked.

References

[1] Java SE 8 - Official Javadoc
https://docs.oracle.com/javase/8/docs/api/