mapjfx 1.7.1 with some minor improvements in offline caching

mapjfx version 1.7.1 is release to maven central; the artifact coordinates are:

  <dependency>
    <groupId>com.sothawo</groupId>
    <artifactId>mapjfx</artifactId>
    <version>1.7.1</version>
  </dependency>

The source is available at GitHub.

This release has two small improvements:

  • the OfflineCache of the MapView now has a clear() method which deletes the entires from cache directory
  • when using OpenStreetmap tiles, files from different servers like a.tile.openstreetmap.org and b.tile.openstreetmap.org are mapped to the same cache file

Comments and contributions welcome.

mapjfx 1.7.0 with the ability for caching map data

I just released mapjfx version 1.7.0 it can be found at maven central, the artifact coordinates are:

  <dependency>
    <groupId>com.sothawo</groupId>
    <artifactId>mapjfx</artifactId>
    <version>1.7.0</version>
  </dependency>

The source is available at GitHub.

The library can now use a directory to cache the downloaded map images for later speedup and offline use. Detailed information can be found here.

Comments and contributions welcome.

Threadsafe simple cache with two lines of code in Java 8

Sometimes you just need a small simple cache for calculated values because the value generation is expensive in time or resources. In this post I show how such a cache can be implemented in a thread safe way with the Java8 version of the ConcurrentHashMap class with just 2 lines of relevant code.

For this example I use the calculation of the n-th fibonacci number and calculate the values for there input of 1 to 20.

using no cache

The sample program and the first version of the fibonacci() method without caching are as follows:

package com.sothawo;

import java.math.BigInteger;

public class App {

    private BigInteger fibonacci(int n) {
        return (n <= 2) ? BigInteger.ONE : fibonacci(n - 1).add(fibonacci(n - 2));
    }

    public static void main(String[] args) {
        new App().run();
    }

    private void run() {
        for (int i = 1; i <= 20; i++) {
            System.out.println("fibonacci(" + i + ") is " + fibonacci(i));
        }
    }
}

This program produces the desired results, but due to the recursion there are 35.400 calls of the fibonacci() method, as already calculated values are recalculated again and again.

pre-Java 8 cache

As a cache I use a HashMap and guard it’s use with a lock object to ensure thread safety. The modified program looks like this:

package com.sothawo;

import java.math.BigInteger;
import java.util.HashMap;
import java.util.Map;

public class App {

    private Map<Integer, BigInteger> simpleCache = new HashMap<>();
    private Object simpleCacheLock = new Object();

    private BigInteger fibonacci(int n) {
        BigInteger fib;
        synchronized (simpleCacheLock) {
            fib = simpleCache.get(n);
            if (null == fib) {
                fib = (n <= 2) ? BigInteger.ONE : fibonacci(n - 1).add(fibonacci(n - 2));
                simpleCache.put(n, fib);
            }
        }
        return fib;
    }

    public static void main(String[] args) {
        new App().run();
    }

    private void run() {
        for (int i = 1; i <= 20; i++) {
            System.out.println("fibonacci(" + i + ") is " + fibonacci(i));
        }
    }
}

This works as expected and gets the number of calls to the fibonacci() method down to the minimum of 20, but is a lot of code.

Java 8 version

The ConcurrentHashMap in Java 8 has got a nice addition, the computeIfAbsent method. It takes two arguments: the key value and a function to calculate the value if not present in the cache. This leads to the following code:

package com.sothawo;

import java.math.BigInteger;
import java.util.concurrent.ConcurrentHashMap;

public class App {
    private ConcurrentHashMap<Integer, BigInteger> betterCache = new ConcurrentHashMap<>();

    private BigInteger fibonacci(int n) {
        return betterCache.computeIfAbsent(n, (i) -> (i <= 2) ? BigInteger.ONE : fibonacci(i - 1).add(fibonacci(i - 2)));
    }

    public static void main(String[] args) {
        new App().run();
    }

    private void run() {
        for (int i = 1; i <= 20; i++) {
            System.out.println("fibonacci(" + i + ") is " + fibonacci(i));
        }
    }
}

The same result as before, but with just two lines of code.