Tuesday, December 27, 2022

Java: Collecting a Stream into an Existing Collection

The following snippet shows how you can collect a stream into an existing collection using Collectors.toCollection:

stream.collect(Collectors.toCollection(() -> existingCollection));

You may also be interested in reading my previous post about collecting a stream into an unmodifiable collection.

Saturday, December 24, 2022

Java 19: Virtual Threads

Java 19 introduces Virtual Threads, which are lightweight threads designed to improve application throughput. This is a preview language feature so must be enabled using --enable-preview.

As you know, the JDK implements platform threads (java.lang.Thread) as thin wrappers around operating system (OS) threads. OS threads are expensive to create, and the number of threads is limited to the number of OS threads that the underlying hardware can support. A platform thread captures the OS thread for the code's entire lifetime.

On the other hand, a virtual thread is an instance of java.lang.Thread that is not tied to a particular OS thread and does not capture the OS thread for the code's entire lifetime. A virtual thread consumes an OS thread only while it performs calculations on the CPU. This means that several virtual threads can run their Java code on the same OS thread, effectively sharing it. When code running in a virtual thread calls a blocking I/O operation, the JVM performs a non-blocking OS call and automatically suspends the virtual thread until it can be resumed later.

Virtual threads help to improve the throughput of thread-per-request style server applications in particular because such applications consist of a great number of concurrent tasks that are not CPU bound and spend much of their time waiting.

Here is an example which creates 10,000 virtual threads; however, the JDK runs the code on perhaps only one OS thread. If we were using 10,000 platform threads (and thus 10,000 OS threads) instead, the program might crash, depending on the hardware available. Virtual threads are cheap and plentiful, and there is no need to pool them.

try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
  IntStream.range(0, 10_000).forEach(i -> {
    executor.submit(() -> {
      Thread.sleep(Duration.ofSeconds(1));
      return i;
    });
  });
}

The java.lang.Thread class has been updated with new methods to create virtual and platform threads, such as:

// create a new unstarted virtual thread named "foo".
Thread.ofVirtual().name("foo").unstarted(runnable);

// create and start a virtual thread
Thread.startVirtualThread(runnable)

Saturday, November 12, 2022

Java 19: Record Patterns

Java 19 introduces Record Patterns, a preview language feature, which allow us to "deconstruct" records and access their components directly.

Here is an example:

record Book(String title, String author, double price) {}

static void print(Book b) {
  if (b instanceof Book(String title, String author, double price)) {
    System.out.printf("%s by %s for %.2f", title, author, price);
  }
}

Book(String title, String author, double price) is a record pattern that decomposes an instance of a Book record into its components: title, author and price.

null does not match any record pattern.

Record patterns can be used in switch statements and expressions as well:

static double getPrice(Publication p) {
  return switch(p) {
    case Book(var title, var author, var price) -> price;
    case Magazine(var title, var publisher, var price) -> price;
    default -> throw new IllegalArgumentException("Invalid publication: " + p);
  };
}

Note that I have used var inside the record patterns, allowing the compiler to infer the type of each component, thus saving me from having to explicitly state it.

Record patterns can also be nested inside one another, in order to decompose complicated object graphs. For example:

record Author(String firstName, String lastName) {}
record Book(String title, Author author, double price) {}

static void print(Book b) {
  if (b instanceof Book(var title, Author(var firstName, var lastName), var price)) {
    System.out.printf("%s by %s %s for %.2f", title, firstName, lastName, price);
  }
}

Saturday, November 05, 2022

Java 19: Guarded Patterns in Switch

Previously, I wrote about how switch statements and expressions had been enhanced to match on type patterns, and also how "guarded patterns" can be used to refine a pattern so that it is only matched on certain conditions.

In Java 19, the syntax of the guarded pattern has been changed so that instead of using &&, you need to use a when clause, as shown in the example below.

static String guardedPattern(Collection<String> coll) {
  return switch(coll) {
    case null -> 
      "Collection is null!";
    case List list 
    when list.size() > 10 ->
      "I am a big List. My size is " + list.size();
    case List list ->
      "I am a small List. My size is " + list.size();
    default -> 
      "Unsupported collection: " + coll.getClass();
  };
}

As an aside, it's worth pointing out how nulls are handled within the switch block. The default label does NOT match nulls, so you need to explicitly add a case null, otherwise you will get a NullPointerException. This is for backwards compatibility with the current semantics of switch.

Related post:
Java 17: Pattern Matching for Switch

Tuesday, May 31, 2022

Java 18: Simple Web Server

Java 18 offers an out-of-the-box simple web server (jwebserver) that serves static files only (no servlet-like functionality or CGI). This tool is useful for prototyping, ad hoc coding, and testing.

To start the server, simply run:

$ jwebserver
Binding to loopback by default. For all interfaces use "-b 0.0.0.0" or "-b ::".
Serving C:\Users\fahd\blog and subdirectories on 127.0.0.1 port 8000
URL http://127.0.0.1:8000/

127.0.0.1 - - [31/May/2022:10:37:31 +0100] "GET / HTTP/1.1" 200 -
127.0.0.1 - - [31/May/2022:10:37:33 +0100] "GET /2022/ HTTP/1.1" 200 -

By default, the server binds to localhost:8000 and serves the current working directory. Every request is logged to the console.

You can change the bind address, port number, directory and logging format using the options shown below:

$ jwebserver --help
Usage: jwebserver [-b bind address] [-p port] [-d directory]
                  [-o none|info|verbose] [-h to show options]
                  [-version to show version information]
Options:
-b, --bind-address    - Address to bind to. Default: 127.0.0.1 (loopback).
                        For all interfaces use "-b 0.0.0.0" or "-b ::".
-d, --directory       - Directory to serve. Default: current directory.
-o, --output          - Output format. none|info|verbose. Default: info.
-p, --port            - Port to listen on. Default: 8000.
-h, -?, --help        - Prints this help message and exits.
-version, --version   - Prints version information and exits.
To stop the server, press Ctrl + C.

To programmatically start the web server from within a java application, you can use the SimpleFileServer.createFileServer method:

import java.net.InetSocketAddress;
import java.nio.file.Path;

import com.sun.net.httpserver.SimpleFileServer;
import com.sun.net.httpserver.SimpleFileServer.OutputLevel;

final var server = SimpleFileServer.createFileServer(
                       new InetSocketAddress(8080), 
                       Path.of("C:\\Users\\fahd\\blog"),
                       OutputLevel.VERBOSE);
server.start();

Alternatively, use HttpServer.create if you wish to pass in your own HTTP handler and filter:

import java.net.InetSocketAddress;
import java.nio.file.Path;

import com.sun.net.httpserver.HttpServer;
import com.sun.net.httpserver.SimpleFileServer;
import com.sun.net.httpserver.SimpleFileServer.OutputLevel;

final var server = HttpServer.create(
              new InetSocketAddress(8000),
              10,
              "/context/",
              SimpleFileServer.createFileHandler(Path.of("C:\\Users\\fahd\\blog")),
              SimpleFileServer.createOutputFilter(System.out, OutputLevel.INFO));
server.start();

Monday, January 03, 2022

Advent of Code 2021

At the end of last year, I took part in Advent of Code, a programming competition that takes place in December every year. It is an Advent calendar of programming puzzles - a new puzzle is released every day from 1-Dec to 25-Dec - and is a great way to test your programming skills and brush up on those algorithms that you don't use very often (like Djikstra's!). There were some really challenging problems and I am pleased that I managed to answer them all using Java. I actually surprised myself on a couple of them because when I first read the question, I didn't think I'd be able to do it. However, I persevered (sometimes even spending the whole day on the problem) and took insipiration from other programmers on the reddit board.

The questions are still available so I would encourage you to have a go!

Bring on Advent of Code 2022!

Saturday, January 01, 2022

fahd.blog in 2021

Happy 2022, everyone!

I'd like to wish everyone a great start to an even greater new year!

In keeping with tradition, here's one last look back at fahd.blog in 2021.

During 2021, I posted 6 new entries on fahd.blog. I am also thrilled that I have more readers from all over the world! Thanks for reading and especially for giving feedback.

Top 3 posts of 2021:

I'm going to be writing a lot more this year, so stay tuned for more great techie tips, tricks and hacks! :)

Related posts: