DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

The software you build is only as secure as the code that powers it. Learn how malicious code creeps into your software supply chain.

Apache Cassandra combines the benefits of major NoSQL databases to support data management needs not covered by traditional RDBMS vendors.

Generative AI has transformed nearly every industry. How can you leverage GenAI to improve your productivity and efficiency?

Modernize your data layer. Learn how to design cloud-native database architectures to meet the evolving demands of AI and GenAI workloads.

Related

  • How to Merge HTML Documents in Java
  • The Future of Java and AI: Coding in 2025
  • Tired of Spring Overhead? Try Dropwizard for Your Next Java Microservice
  • Using Python Libraries in Java

Trending

  • FIPS 140-3: The Security Standard That Protects Our Federal Data
  • Beyond Code Coverage: A Risk-Driven Revolution in Software Testing With Machine Learning
  • Unmasking Entity-Based Data Masking: Best Practices 2025
  • Accelerating Debugging in Integration Testing: An Efficient Search-Based Workflow for Impact Localization
  1. DZone
  2. Coding
  3. Java
  4. Java: Gain Performance Using SingletonStream

Java: Gain Performance Using SingletonStream

Java streams sometimes create unnecessary overhead in your applications. Learn how to use SingletonStream objects and gain over tenfold performance in this post!

By 
Per-Åke Minborg user avatar
Per-Åke Minborg
·
Updated Oct. 10, 18 · Tutorial
Likes (6)
Comment
Save
Tweet
Share
11.0K Views

Join the DZone community and get the full member experience.

Join For Free

Background

The Stream library in Java 8 is one of the most powerful additions to the Java language ever. Once you start to understand its versatility and resulting code readability, your Java code-style will change forever. Instead of bloating your code with all the nitty and gritty details with for, if, and switch statements and numerous intermediate variables, you can use a Stream that just contains a description of what to do, and not really how it is done.

Some years ago, we had to make an API decision for a Java project: which return type should we select for the two fast local in-memory data cache methods with;

  • A unique search key which returns either a value or no value
  • A non-unique search key which returns any number of values (zero to infinity).

This was the initial idea:

Optional<T> searchUnique(K key); // For unique keys
Stream<T> search(K key);         // For non-unique keys


But, we would rather have the two methods look exactly the same and both return a Stream<T>. The API would then look much cleaner because a unique cache would then look exactly the same as a non-unique cache.

However, the unique search had to be very efficient and able to create millions of result objects each second without creating too much overhead.

Solution

By implementing a SingletonStream that only takes a single element, and therefore, can be highly optimized compared to a normal Stream with any number of elements, we were able to let both methods return a Stream while retaining performance. The method searchUnique(K key) would return an empty stream (Stream.empty()) if the key was not found, and it would return a SingletonStream with the value associated with the key if the key existed. We would get:

Stream<T> searchUnique(K key); // For unique keys
Stream<T> search(K key);       // For non-unique keys


Great! We can eat the cookie and still have it!

The Implementation

The SingletonStream is a part of the Speedment Stream ORM and can be viewed here on GitHub. Feel free to use Speedment and any of it's component in your own projects using the Speedment initializer.

The SingletonStream is a good candidate for stack allocation using the JVM's Escape Analysis (read more on Escape Analysis in my previous posts here and here). The implementation comes in two shapes. if we set the STRICT value to true, we will get a completely lazy Stream, but the drawback is that we will lose the Singleton Property once we call some Intermediate Operations, like .filter(), map(), etc. If we, on the other hand, set the STRICT value to false, the SingletonStream will perform many of the Intermediate Operations eagerly and it will be able to return a new SingletonStream, thereby retaining the Singleton Property. This will give better performance in many cases.

The solution devised here for reference streams could also easily be modified to the primitive incarnations of singleton streams. So, it would be almost trivial to write a SingletonIntStream, a SingletonLongStream and a SingletonDoubleStream. Here is a SingletonLongStream.

It should be noted that the class could be further developed so it could support lazy evaluation while still being always high performant. This is a future work.

Performance

There are many ways one could test the performance of the SingletonStream and compare it with a standard Stream implementation with one element.

Here is one way of doing it using JMH. The first tests (count) just counts the number of elements in the stream and the second tests (forEach) does something with one element of a stream.

@Benchmark
public long singletonStreamCount() {
    return SingletonStream.of("A").count();
}

@Benchmark
public long streamCount() {
    return Stream.of("A").count();
}

@Benchmark
public void singletonStreamForEach() {
    SingletonStream.of("A")
        .limit(1)
        .forEach(blackHole());
}

@Benchmark
public void streamForEach() {
   Stream.of("A")
        .limit(1)
        .forEach(blackHole());
}

private static <T> Consumer<T> blackHole() {
    return t -> {};
}


This will produce the following result when run on my MacBook Pro laptop:

...
Benchmark                               Mode  Cnt           Score   Error  Units
SingletonBench.singletonStreamCount    thrpt        333419753.335          ops/s
SingletonBench.singletonStreamForEach  thrpt       2312262034.214          ops/s
SingletonBench.streamCount             thrpt         27453782.595          ops/s
SingletonBench.streamForEach           thrpt         26156364.956          ops/s
...


That's a speedup factor over 10 for the count operation. For the forEach operation, it looks like the JVM was able to completely optimize away the complete code path for the SingletonStream.

Test It

The complete test class is available here.

Conclusion

The SingletonStream works more or less as an extended Optional and allows high performance while retaining the benefits of the Stream library.

You can select two versions of it by setting the STRICT value to your preferred stringency/performance choice.

The SingletonStream could be further improved.

Java (programming language)

Published at DZone with permission of Per-Åke Minborg, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

Related

  • How to Merge HTML Documents in Java
  • The Future of Java and AI: Coding in 2025
  • Tired of Spring Overhead? Try Dropwizard for Your Next Java Microservice
  • Using Python Libraries in Java

Partner Resources

×

Comments

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends: