Java Specialists' Java Training Europehome of the java specialists' newsletter

The Java Specialists' Newsletter
Issue 2122013-07-19 Category: Language Java version: Java 6,7,8

GitHub Subscribe Free RSS Feed

Creating Sets from Maps

by Dr. Heinz M. Kabutz
Abstract:
Maps and Sets in Java have some similarities. In this newsletter we show a nice little trick for converting a map class into a set.

Welcome to the 212th issue of The Java(tm) Specialists' Newsletter, sent to you from the wonderful Island of Crete. Three weeks ago, I had the great privilege of being interviewed for Greek citizenship. The interview was in Greek, which meant I had to be able to both understand the question and then formulate an answer in at least passable Greek. I also had to be able to read and write Greek. I started learning a bit of Greek to impress my then future father-in-law in 1991. I have also lived in Greece for the last seven years. So if I had any brain cells at all, I really should have aced the interview. Unfortunately my worst subjects at school were history and geography, which most of the questions were about. As of writing, I do not know whether I passed or failed. All I know is that I enjoyed the panel interview with five examiners. And I think they also had fun, since they kept me there for 50 minutes. The other candidates were done in just 25. We ended up talking about Java, our Cretan Java Unconference and many other topics that were not really in their set of questions. If I failed, I'll be invited back next year again for another fun session of discussing Greek history, politics and geography.

NEW: Please see our new "Extreme Java" course, combining concurrency, a little bit of performance and Java 8. Extreme Java - Concurrency & Performance for Java 8.

Creating Sets from Maps

Seeing that it is summer here in Crete, this will be a short newsletter. Just one small tip that I picked up a few months ago and that has surprised quite a few people.

You probably know that the HashSet is just a wrapper around the HashMap, with the Set methods exposed. Similarly, the TreeSet is a wrapper around the TreeMap. The Map does not allow us to iterate over it directly. Thus the following would not work:

Map<String, Double> salaries = new HashMap<>();
for(double salary : salaries) { // does not compile
}
  

We can iterate over the set of keys, over the collection of values or over the set of entries. Values can have duplicates, which is why a collection is returned. Keys can never have duplicates, so both the keys and the entries can be returned as sets.

Thus if we wanted to iterate over just the values, we could do this:

Map<String, Double> salaries = new HashMap<>();
for (double salary : salaries.values()) {
}
  

Or over the keys would be:

Map<String, Double> salaries = new HashMap<>();
for (String name : salaries.keySet()) {
}
  

And lastly, over the entries would be:

Map<String, Double> salaries = new HashMap<>();
for (Map.Entry<String, Double> entry : salaries.entrySet()) {
  String name = entry.getKey();
  double salary = entry.getValue();
}
  

An approach I have often seen is when programmers get the keySet and then iterate through the set to find the values with get(), such as:

Map<String, Double> salaries = new HashMap<>();
for (String name : salaries.keySet()) { // less efficient way to
  double salary = salaries.get(name);   // iterate over entries
}
  

Intuitively, it seems to me that the approach of iterating over the entry set should be much faster. The iteration is O(n). However, if the HashMap is well balanced, then we should get O(1) performance on the lookup call to get(), thus the complexity of the two approaches is the same. There might be a small difference, but they should both scale linearly. This is not true with other types of maps, such as the TreeMap. The lookup complexity is O(log n), thus the complexity of the second loop would be O(n x log n).

There are lots of maps available and for some of them, as we saw already with TreeMap and HashMap, we have corresponding set implementations. These sets are thin wrappers around the Map and thus have the same complexity and concurrency behaviour.

Now for the tip. In one of the exercises for my Concurrency Specialist Course I needed a fast, thread-safe HashSet. Initially, I used a ConcurrentHashMap with a dummy value. However, in Java 6, a method newSetFromMap() was added to the java.util.Collections class for creating new sets based on maps. The generic type parameter K of the map should be the element type inside the set and the V should be a Boolean, used to confirm the presence of the element.

import java.util.*;
import java.util.concurrent.*;

public class ConcurrentSetTest {
  public static void main(String[] args) {
    Set<String> names = Collections.newSetFromMap(
        new ConcurrentHashMap<String, Boolean>()
    );
    names.add("Brian Goetz");
    names.add("Victor Grazi");
    names.add("Heinz Kabutz");
    names.add("Brian Goetz");
    System.out.println("names = " + names);
  }
}
  

The newSetFromMap() method obviously only exposes the methods that are in Set. Thus if your map has a richer interface than the standard Map, you would need to still write your own wrapper for it.

I hope you enjoyed this little snippet of information. If you ever looked for the ConcurrentHashSet, now you know how to get one.

Kind regards

Heinz

Language Articles Related Java Course

Extreme Java - Concurrency and Performance for Java 8
Extreme Java - Advanced Topics for Java 8
Design Patterns
In-House Courses

© 2010-2016 Heinz Kabutz - All Rights Reserved Sitemap
Oracle and Java are registered trademarks of Oracle and/or its affiliates. Other names may be trademarks of their respective owners. JavaSpecialists.eu is not connected to Oracle, Inc. and is not sponsored by Oracle, Inc.