Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Key Collections powered by Java 8 Lambdas

DZone's Guide to

Key Collections powered by Java 8 Lambdas

· Java Zone
Free Resource

Check out this 8-step guide to see how you can increase your productivity by skipping slow application redeploys and by implementing application profiling, as you code! Brought to you in partnership with ZeroTurnaround.

The Brownies collections library is primarily known for the fastest List implementation  around, but it also contains key collections which bring the power of keys and constraints to Java collections.

The ability to combine all functionality in a declarative and orthogonal way makes the key collections as versatile as a Swiss army knife. The only drawback was the clumsy syntax needed to specify key mappings or constraints.

Here Java 8 come to the rescue: The new lambdas allow use to constructs all collections easily with a just a few statements. Let's look at this concrete example:

  • maintain a bidirectional mapping between zip code and city name

  • zip codes are unique, but one city name can be associated with several zip codes

  • store both zip codes and city names sorted

So our collection will then store data like this:

  • Zip 4000 → City "Basel"

  • Zip 5000 → City "Aarau"

  • Zip 5001 → City "Aarau"

What are our options for implementation? Clearly this must be an easy task if the requirements can be written down in three lines...

Java Collection Framework

Let's start with the Java collections framework: There is no data structure which fits exactly so we must combine some of the existing ones. To store zip codes and city names sorted, we could use two TreeMaps which must be kept in synch. And extra care must be taken to model 1:N relationship correctly...

Guava

One of the usual suspects for providing commonly used stuff is Guava. It also offers a lot of collections stuff, including an interface called BiMap. The Guava BiMaps can however only be used for 1:1 releationships and there is also no possibility to have both maps sorted...

Commons Collections

Our next candidate is Commons Collections. At first sight, things look pretty good as there is a DualTreeBidiMap. Howerver also this map is only capable of storing 1:1 relationships...

Brownies Collections

Obviously this task seems to be somewhat more complicated than we have thought first. How can the key collections help?

The key collections do not support a Map or a special BiMap interface, but allow access to collections elements by exposed keys. So to store the needed data, we define this simple class:

class ZipCity {
        int zip;
        String city;

        public ZipCity(int zip, String city) {
            this.zip = zip;
            this.city = city;
        }
        public int getZip() {
            return zip;
        }
        public String getCity() {
            return city;
        }
    }

Our collection will then store instances of this class which we need to access by two keys: zip code (an integer) and city name (a string). We therfore create a collection with two keys, hence

  • Key2Collection<ZipCity, Integer, String>

For these two keys, we define the mapping used to extract the key values out of the collection element:

  • withKey1Map(ZipCity::getZip)

  • withKey2Map(ZipCity::getCity)

With this declaration, we already set up the synchronization between object and keys. Let's add the other features as well.

  • Zip code must be non null and unique: withPrimaryKey1()

  • City name must be non null: withKey2Null(false)

  • Both zip codes and names must be sorted: withKey1Sort(true), withKey2Sort(true)

And that's all! How does this work internally? The key collections are using standard Java collections under the hood to implement the functionality. In our example, the Key2Collection contains one HashSet (for the elements) and two TreeMaps which map the sorted keys to the elements in the set. This can be seen on the following illustration:



The complete listing at the end of the article shows how the required functionality can be implemented with just a few lines of Java 8 code.

To get an overview all features and classes, have a look at the new graphical overview of classes and functionality which can be used like a cheat sheet. You can access all example code with snippets for Java 8 directly from the image.

Listing


class ZipCity {
	int zip;
	String city;

	public ZipCity(int zip, String city) {
		this.zip = zip;
		this.city = city;
	}
	public int getZip() {
		return zip;
	}
	public String getCity() {
		return city;
	}
}

void testZipCity() {
	Key2Collection<ZipCity,Integer,String> zipCities = new Key2Collection.Builder<ZipCity,Integer,String>().
		withKey1Map(ZipCity::getZip).
		withPrimaryKey1().
		withKey1Sort(true).
		withKey2Map(ZipCity::getCity).
		withKey2Null(false).
		withKey2Sort(true).build();

	zipCities.add(new ZipCity(4000, "Basel"));
	zipCities.add(new ZipCity(5000, "Aarau"));
	zipCities.add(new ZipCity(5001, "Aarau"));

	// Get all zips sorted
	Set<Integer> allZips = zipCities.asMap1().keySet();

	// Get all cities sorted
	Set<String> allCities = zipCities.asMap2().keySet();

	// Get city by zip
	String city = zipCities.getByKey1(5000).getCity();

	// Get all zips by city
	GapList<Integer> zips = zipCities.getAllByKey2("Aarau").map(ZipCity::getZip);
}

The Java Zone is brought to you in partnership with ZeroTurnaround. Check out this 8-step guide to see how you can increase your productivity by skipping slow application redeploys and by implementing application profiling, as you code!

Topics:

Opinions expressed by DZone contributors are their own.

The best of DZone straight to your inbox.

SEE AN EXAMPLE
Please provide a valid email address.

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.
Subscribe

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}