avatarAleksandra Liutikova aka Java Senorita

Free AI web copilot to create summaries, insights and extended knowledge, download it at here

6101

Abstract

</span> <span class="hljs-operator">=</span> map.get(<span class="hljs-number">1</span>); <span class="hljs-comment">// value will be "one"</span>

<span class="hljs-comment">// Close the DB to prevent memory leaks</span>
db.close();

}</pre></div><p id="ffe6">MapDB’s ability to provide disk-backed, thread-safe collections makes it an exceptional choice for applications requiring large data structures but where the luxury of a full-fledged database system is unnecessary or desired.</p><p id="1a7e">JDBI and MapDB exemplify the innovative solutions offered by lesser-known libraries in the Java ecosystem. They demonstrate that data handling and persistence can be significantly more efficient and tailored to the application’s needs with the right tools.</p><h1 id="9ec1">Functional Programming and Concurrency</h1><p id="acbb">As Java evolves, so does how we handle operations that can occur simultaneously (<b>concurrency</b>) and how we manipulate data (<b>functional programming</b>). While Java 8 introduced lambda expressions and the Stream API, a whole world of functional programming can be unlocked with the right tools. <a href="https://www.vavr.io/">Vavr</a> and <a href="https://docs.paralleluniverse.co/quasar/">Quasar</a> are two such libraries that bring additional power and elegance to Java’s capabilities in these areas.</p><h2 id="e993">Vavr: Embracing Immutability and Functional Constructs</h2><p id="c985"><a href="https://www.vavr.io">Vavr</a> library provides persistent collections, functional abstractions for error handling, concurrent programming, pattern matching, and more. This means that once you create a data structure, it can’t be changed, and Vavr allows you to work with this kind of effectively. Immutable objects are inherently thread-safe and can be used without fear of concurrent modification exceptions. On top of that, all Vavr collections enrich the functionality of Java collections and do not use any external libraries, so it keeps your code and project clean.</p><p id="c94c">The motto of this library is</p><p id="d170" type="7">vavr — turns java™ upside down</p><p id="333c">Let’s explore how Vavr can be used to create a list that is summed up without being converted to stream:</p><div id="f15a"><pre><span class="hljs-comment">// Import Vavr classes</span> <span class="hljs-keyword">import</span> io.vavr.collection.List;

<span class="hljs-comment">// Sum up the list without creating a stream for that!</span> List.of(<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>).sum();</pre></div><p id="13f9">This example shows how Vavr enables a functional programming style where you work with values efficiently.</p><h2 id="090d">Quasar: Lightweight Threads and Actors for Java</h2><p id="7fb8">Concurrency can be tricky in Java, but <a href="https://docs.paralleluniverse.co/quasar/">Quasar</a> aims to simplify it by providing lightweight threads (fibers), actors, channels, and other concurrency tools. This allows developers to write asynchronous, scalable, and parallel applications more efficiently.</p><p id="c952">This sounds like magic because anyone who tried to write an excellent asynchronous code in Java can tell it is very complex and messy, but Quasar makes it look great!</p><p id="54c7">Here’s an example of using Quasar fibers, which are like Java threads but much lighter in terms of memory and overhead:</p><div id="7c81"><pre><span class="hljs-comment">// Import co.paralleluniverse.fibers.Suspendable</span> <span class="hljs-keyword">import</span> co.paralleluniverse.fibers.Fiber; <span class="hljs-keyword">import</span> co.paralleluniverse.fibers.Suspendable;

<span class="hljs-comment">// Define a method as suspendable which can be paused and resumed</span> <span class="hljs-meta">@Suspendable</span> <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">performComputation</span><span class="hljs-params">()</span> { <span class="hljs-comment">// Code that performs computation here</span> System.out.println(<span class="hljs-string">"Computation done by fiber!"</span>); }

<span class="hljs-comment">// Start a new fiber that runs the computation</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">Fiber</span><Void>(() -> performComputation()).start();</pre></div><p id="2802">The above code creates a new fiber that runs <code>performComputation</code>. Unlike standard Java threads, creating and switching between many fibers has no significant performance cost.</p><p id="7be1">Quasar extends these concepts with actors, a concurrency model that treats “actors” as the fundamental computation unit. Actors can send messages to each other and manage their state, avoiding many problems of a shared mutable state.</p><p id="7904">Using both Vavr and Quasar, Java developers can tackle complex problems in concurrency and functional programming with a more expressive and safer codebase. These libraries don’t just simplify code — they often make it more robust and performant.</p><h1 id="b116">Utilities and Complementary Tools</h1><p id="988d">While building applications, developers often encounter repetitive patterns and common problems requiring utility solutions. Java has a robust standard library, but it does not cover some specific utilities. That’s where libraries like <a href="https://www.javatuples.org/">Javatuples</a> and <a href="http://www.awaitility.org/">Awaitility</a> can be game-changers, filling in the gaps and providing additional functionality to make a developer’s life easier.</p><h2 id="3acf">Javatuples: Embracing Simplicity with Typesafe Tuples</h2><p id="d877">In many programming tasks, there’s a need to return multiple values from a method or to temporarily group a set of objects without creating a specific class. Tuples are perfect for these scenarios. What is a tuple? It is just a sequence of objects that do not necessarily relate to each other in any way.</p><p id="d7fd">For example: <code>[23, “Saturn

Options

”, java.sql.Connection@li734s]</code> can be considered a tuple of three elements (a <i>triplet</i>) containing an <code>Integer</code>, a <code>String</code>, and a<code> JDBC Connection</code> object. As simple as that.</p><p id="f52d"><a href="https://www.javatuples.org">Javatuples</a> is a tiny and clean library that brings the power and simplicity of tuples to Java, which does not have built-in support for those.</p><p id="d4f0">Here’s how you can use Javatuples to return two different values from a method:</p><div id="5f76"><pre><span class="hljs-comment">// Import Pair class from javatuples</span> <span class="hljs-keyword">import</span> org.javatuples.Pair;

<span class="hljs-comment">// A method that returns two values as a tuple</span> <span class="hljs-keyword">public</span> Pair<String, Integer> <span class="hljs-title function_">getUserNameAndAge</span><span class="hljs-params">()</span> { <span class="hljs-comment">// Assume these values come from some operation</span> <span class="hljs-type">String</span> <span class="hljs-variable">name</span> <span class="hljs-operator">=</span> <span class="hljs-string">"Alice"</span>; <span class="hljs-type">Integer</span> <span class="hljs-variable">age</span> <span class="hljs-operator">=</span> <span class="hljs-number">30</span>; <span class="hljs-comment">// Return a pair of name and age</span> <span class="hljs-keyword">return</span> Pair.with(name, age); }

<span class="hljs-comment">// Using the getUserNameAndAge method</span> Pair<String, Integer> userInfo = getUserNameAndAge(); System.out.println(<span class="hljs-string">"Name: "</span> + userInfo.getValue0()); System.out.println(<span class="hljs-string">"Age: "</span> + userInfo.getValue1());</pre></div><p id="50fc">The Pair class from Javatuples allows you to store two related values of different types and to return them from a method without creating a custom object.</p><h2 id="6f00">Awaitility: Testing Asynchronous Operations with Ease</h2><p id="426c">Testing asynchronous operations can be tricky; you often have to deal with threads and ensure that the state is synchronized before asserting the outcome. <a href="http://www.awaitility.org">Awaitility</a> is a DSL that simplifies the process, allowing you to wait for asynchronous operations to complete in a readable and typesafe way.</p><p id="852e">Consider an example where you have an asynchronous operation that increments a counter, and you want to test that the operation completes successfully:</p><div id="3bdd"><pre><span class="hljs-comment">// Import Awaitility methods</span> <span class="hljs-keyword">import</span> <span class="hljs-keyword">static</span> org.awaitility.Awaitility.await; <span class="hljs-keyword">import</span> java.util.concurrent.atomic.AtomicInteger;

<span class="hljs-comment">// A counter to be incremented asynchronously</span> <span class="hljs-type">AtomicInteger</span> <span class="hljs-variable">counter</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">AtomicInteger</span>(<span class="hljs-number">0</span>);

<span class="hljs-comment">// An asynchronous operation that increments the counter</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">Thread</span>(() -> { <span class="hljs-comment">// Simulate some computation</span> <span class="hljs-keyword">try</span> { Thread.sleep(<span class="hljs-number">500</span>); <span class="hljs-comment">// wait for 500 milliseconds</span> } <span class="hljs-keyword">catch</span> (InterruptedException e) { Thread.currentThread().interrupt(); } counter.incrementAndGet(); }).start();

<span class="hljs-comment">// Use Awaitility to wait until the counter is incremented</span> await().until(() -> counter.get() == <span class="hljs-number">1</span>);

<span class="hljs-comment">// Now you can safely assert that the counter has been incremented</span> System.out.println(<span class="hljs-string">"Counter value: "</span> + counter.get());</pre></div><p id="ffdb">Awaitility takes care of the waiting mechanism, checking the condition at regular intervals, and proceeds only when the condition is met or a specified timeout is reached. You no longer need to write complicated thread synchronization code in your tests.</p><p id="175e">Javatuples and Awaitility demonstrate how small libraries can provide robust solutions to specific Java problems. By integrating these tools, developers can write more transparent, robust code and spend less time-solving problems that have already been effectively addressed.</p><h1 id="e782">Conclusion</h1><p id="797a">The Java ecosystem is rich with libraries that reach beyond the well-known path of established giants, offering elegant solutions to complex programming challenges. Libraries like JDBI and MapDB revolutionize data handling quickly and efficiently, while Vavr and Quasar introduce robust functional programming and concurrency models that simplify code and enhance performance. Utilities such as Javatuples and Awaitility address specific needs precisely, enabling developers to write cleaner, more maintainable code.</p><p id="28fa">Exploring these lesser-known libraries can be immensely rewarding, providing developers with the tools to craft more effective Java applications. Incorporating these tools into your development toolkit can lead to more innovative, responsive, and efficient software creation, demonstrating that sometimes the most valuable treasures are hidden beneath the surface.</p><p id="af83">This is the first of the few articles on the topic. Follow me to stay tuned; after this cycle of articles, your code won’t be the same anymore!</p><p id="e024">Until next time, keep coding, innovating, and never stop learning. Cheers, fellow developers!</p><p id="6357">If you enjoyed reading my article, please consider <a href="https://www.buymeacoffee.com/javasenorita"><b><i>buying me a coffee</i></b></a> 💗 and stay tuned to more articles about Java, tech, and AI 👩🏻‍💻</p></article></body>

The Alternate Java Library Guide: Libraries You Haven’t Heard Of But You Should — part one

An image generated for this article by Adobe FireFly

What do I consider a library that you should try?

In various Java libraries, some shine brightly and have become an industry standard — think of pillars like Apache Commons, Google’s Guava, or JUnit. Then, some libraries are “lesser-known”, and though they may not headline every developer conference or star on GitHub’s trending page, they have the potential to be just as valuable in the right circumstances.

What makes a library “lesser-known”? It isn’t simply a matter of raw download statistics or GitHub stars. A lesser-known library may be pretty popular within niche circles but hasn’t yet broken into the mainstream consciousness of the average Java developer. Perhaps it’s a new library that’s still gathering momentum, or maybe it serves a particular purpose that not every developer needs — but those who do cherish it deeply.

To choose the libraries I’ll discuss in this article, I considered the following criteria:

  • Community Support: While the library may have little recognition, it has a dedicated user base or a small, active community contributing to its maintenance and evolution. This ensures that you are covered with resources when you adopt.
  • Documentation Quality: For a library to be recommended, it must have enough documentation to allow a beginner to install it and use its essential features.
  • Unique Functionality: There’s something special about each library selected that fills a gap in the Java ecosystem. This could be an innovative approach to a common problem or a niche functionality that’s hard to find elsewhere.

Data Handling and Persistence

Data handling and persistence are fundamental aspects of many Java applications. Java developers often turn to established solutions like JDBC, Hibernate, or JPA for these tasks. However, lesser-known libraries offer unique and convenient ways to manage and persist data. Let’s explore two such libraries: JDBI and MapDB.

JDBI: The Java Database Interface

JDBI is a library that bridges traditional JDBC and the more modern, conversational coding in Java. It allows you to interact with your database with less boilerplate code while maintaining direct access to its features and functions.

Here is how you can use JDBI to query a list of users from a database:

// Import JDBI core package
import org.jdbi.v3.core.Jdbi;

// Create a JDBI instance connected to your database
Jdbi jdbi = Jdbi.create("jdbc:h2:mem:test");

// Use a Handle to query the database
jdbi.useHandle(handle -> {
  // Perform a query that returns a list of User objects
  List<User> users = handle.createQuery("SELECT id, name, email FROM users")
  .mapToBean(User.class)
  .list();

  // You can now work with the list of User objects
  users.forEach(System.out::println);
});

With JDBI, you can easily bind Java objects to SQL operations, making database interactions feel like natural Java coding. It does not hide anything from your eyes as most ORMs do, so you still have complete control over your code and a clean syntax on the same side.

MapDB: Collections Backed by On-Disk Storage

MapDB provides Java collections that are stored directly on disk. This can be extremely useful when dealing with large datasets that do not fit into RAM. MapDB’s collections can be used just like java.util collections, but they have the power to handle large amounts of data with a small memory footprint.

Here’s an example of creating and using a MapDB-backed map:

// Import MapDB classes
import org.mapdb.DB;
import org.mapdb.DBMaker;
import org.mapdb.Serializer;

public class MapdbTest {
    // Create or open an on-disk database file
    DB db = DBMaker.fileDB("file.db")
            .fileMmapEnable()
            .make();

    // Open an existing HashMap (or create a new one)
    ConcurrentMap<Integer, String> map = db
            .hashMap("map", Serializer.INTEGER, Serializer.STRING)
            .createOrOpen();

    // Use the map with on-disk persistence
        map.put(1,"one");
        map.put(2,"two");

    // Read from the map
    String value = map.get(1); // value will be "one"

    // Close the DB to prevent memory leaks
    db.close();
}

MapDB’s ability to provide disk-backed, thread-safe collections makes it an exceptional choice for applications requiring large data structures but where the luxury of a full-fledged database system is unnecessary or desired.

JDBI and MapDB exemplify the innovative solutions offered by lesser-known libraries in the Java ecosystem. They demonstrate that data handling and persistence can be significantly more efficient and tailored to the application’s needs with the right tools.

Functional Programming and Concurrency

As Java evolves, so does how we handle operations that can occur simultaneously (concurrency) and how we manipulate data (functional programming). While Java 8 introduced lambda expressions and the Stream API, a whole world of functional programming can be unlocked with the right tools. Vavr and Quasar are two such libraries that bring additional power and elegance to Java’s capabilities in these areas.

Vavr: Embracing Immutability and Functional Constructs

Vavr library provides persistent collections, functional abstractions for error handling, concurrent programming, pattern matching, and more. This means that once you create a data structure, it can’t be changed, and Vavr allows you to work with this kind of effectively. Immutable objects are inherently thread-safe and can be used without fear of concurrent modification exceptions. On top of that, all Vavr collections enrich the functionality of Java collections and do not use any external libraries, so it keeps your code and project clean.

The motto of this library is

vavr — turns java™ upside down

Let’s explore how Vavr can be used to create a list that is summed up without being converted to stream:

// Import Vavr classes
import io.vavr.collection.List;

// Sum up the list without creating a stream for that!
List.of(1, 2, 3).sum();

This example shows how Vavr enables a functional programming style where you work with values efficiently.

Quasar: Lightweight Threads and Actors for Java

Concurrency can be tricky in Java, but Quasar aims to simplify it by providing lightweight threads (fibers), actors, channels, and other concurrency tools. This allows developers to write asynchronous, scalable, and parallel applications more efficiently.

This sounds like magic because anyone who tried to write an excellent asynchronous code in Java can tell it is very complex and messy, but Quasar makes it look great!

Here’s an example of using Quasar fibers, which are like Java threads but much lighter in terms of memory and overhead:

// Import co.paralleluniverse.fibers.Suspendable
import co.paralleluniverse.fibers.Fiber;
import co.paralleluniverse.fibers.Suspendable;

// Define a method as suspendable which can be paused and resumed
@Suspendable
public void performComputation() {
// Code that performs computation here
  System.out.println("Computation done by fiber!");
}

// Start a new fiber that runs the computation
new Fiber<Void>(() -> performComputation()).start();

The above code creates a new fiber that runs performComputation. Unlike standard Java threads, creating and switching between many fibers has no significant performance cost.

Quasar extends these concepts with actors, a concurrency model that treats “actors” as the fundamental computation unit. Actors can send messages to each other and manage their state, avoiding many problems of a shared mutable state.

Using both Vavr and Quasar, Java developers can tackle complex problems in concurrency and functional programming with a more expressive and safer codebase. These libraries don’t just simplify code — they often make it more robust and performant.

Utilities and Complementary Tools

While building applications, developers often encounter repetitive patterns and common problems requiring utility solutions. Java has a robust standard library, but it does not cover some specific utilities. That’s where libraries like Javatuples and Awaitility can be game-changers, filling in the gaps and providing additional functionality to make a developer’s life easier.

Javatuples: Embracing Simplicity with Typesafe Tuples

In many programming tasks, there’s a need to return multiple values from a method or to temporarily group a set of objects without creating a specific class. Tuples are perfect for these scenarios. What is a tuple? It is just a sequence of objects that do not necessarily relate to each other in any way.

For example: [23, “Saturn”, java.sql.Connection@li734s] can be considered a tuple of three elements (a triplet) containing an Integer, a String, and a JDBC Connection object. As simple as that.

Javatuples is a tiny and clean library that brings the power and simplicity of tuples to Java, which does not have built-in support for those.

Here’s how you can use Javatuples to return two different values from a method:

// Import Pair class from javatuples
import org.javatuples.Pair;

// A method that returns two values as a tuple
public Pair<String, Integer> getUserNameAndAge() {
  // Assume these values come from some operation
  String name = "Alice";
  Integer age = 30;
  // Return a pair of name and age
  return Pair.with(name, age);
}

// Using the getUserNameAndAge method
Pair<String, Integer> userInfo = getUserNameAndAge();
System.out.println("Name: " + userInfo.getValue0());
System.out.println("Age: " + userInfo.getValue1());

The Pair class from Javatuples allows you to store two related values of different types and to return them from a method without creating a custom object.

Awaitility: Testing Asynchronous Operations with Ease

Testing asynchronous operations can be tricky; you often have to deal with threads and ensure that the state is synchronized before asserting the outcome. Awaitility is a DSL that simplifies the process, allowing you to wait for asynchronous operations to complete in a readable and typesafe way.

Consider an example where you have an asynchronous operation that increments a counter, and you want to test that the operation completes successfully:

// Import Awaitility methods
import static org.awaitility.Awaitility.await;
import java.util.concurrent.atomic.AtomicInteger;

// A counter to be incremented asynchronously
AtomicInteger counter = new AtomicInteger(0);

// An asynchronous operation that increments the counter
new Thread(() -> {
  // Simulate some computation
  try {
  Thread.sleep(500); // wait for 500 milliseconds
  } catch (InterruptedException e) {
  Thread.currentThread().interrupt();
}
counter.incrementAndGet();
}).start();

// Use Awaitility to wait until the counter is incremented
await().until(() -> counter.get() == 1);

// Now you can safely assert that the counter has been incremented
System.out.println("Counter value: " + counter.get());

Awaitility takes care of the waiting mechanism, checking the condition at regular intervals, and proceeds only when the condition is met or a specified timeout is reached. You no longer need to write complicated thread synchronization code in your tests.

Javatuples and Awaitility demonstrate how small libraries can provide robust solutions to specific Java problems. By integrating these tools, developers can write more transparent, robust code and spend less time-solving problems that have already been effectively addressed.

Conclusion

The Java ecosystem is rich with libraries that reach beyond the well-known path of established giants, offering elegant solutions to complex programming challenges. Libraries like JDBI and MapDB revolutionize data handling quickly and efficiently, while Vavr and Quasar introduce robust functional programming and concurrency models that simplify code and enhance performance. Utilities such as Javatuples and Awaitility address specific needs precisely, enabling developers to write cleaner, more maintainable code.

Exploring these lesser-known libraries can be immensely rewarding, providing developers with the tools to craft more effective Java applications. Incorporating these tools into your development toolkit can lead to more innovative, responsive, and efficient software creation, demonstrating that sometimes the most valuable treasures are hidden beneath the surface.

This is the first of the few articles on the topic. Follow me to stay tuned; after this cycle of articles, your code won’t be the same anymore!

Until next time, keep coding, innovating, and never stop learning. Cheers, fellow developers!

If you enjoyed reading my article, please consider buying me a coffee 💗 and stay tuned to more articles about Java, tech, and AI 👩🏻‍💻

Java
Programming
Coding
Java Library
Java Tutorial
Recommended from ReadMedium