ZOFTINO.COM android and web dev tutorials

Java Collections Map Tutorial

Map is a part of java collections framework and it holds keys and its values. For each key, there can be only one value. Map can’t contain duplicate keys. Map interface defines methods to add and remove key and value pairs. Java collections framework provides HashMap, TreeMap, and LinkedHashMap implementations of Map interface.

SortedMap interface extends Map and maintains keys in ascending order. Keys are ordered in natural order or based on the comparator passed to its constructor. In addition to supporting Map operations, SortedMap provides range operations such as subMap, headMap, tailMap, etc. NavigableMap interface extends SortedMap and defines navigation methods.

Like other collection objects such as list and set, Map objects are instantiated by defining the data type of key and values, see generics for more information.

Table of Contents

Java Map Hierarch

java map hierarchy

HashMap

HashMap is an implementation of Map interface. It allows null key and values. HashMap doesn’t order elements. HashMap can be instantiated by passing initial capacity and load factor to it constructor. Initial capacity specifies the number of hastable buckets to be created when hash map is instantiated and load factor is used to decide when to increase the capacity. These parameters impact the performance of HashMap. Default load factor value is 0.75.

	Map<Integer, String> map = new HashMap<Integer, String>();
	
	Map<Integer, String> mapOne = new HashMap<Integer, String>(10);
	
	Map<Integer, String> mapTwo = new HashMap<Integer, String>(10, 70);

HashMap is not thread safe. If multiple threads share an object of HashMap, you need to get the synchronized hash map object as shown below or you can use concurrent hash map.

Map<Integer, String> synchronizedMap = Collections.synchronizedMap(map);

Following section uses HashMap to show how to use Map operations.

Adding Objects to Map

Method put is used to add a key and associated value to a map. Following example shows how to add objects to a map. It also shows based on the output that HashMap doesn’t order its keys.

	Map<String, Integer> storeCoupons = new HashMap<String, Integer>();

	storeCoupons.put("amazon", 22);
	storeCoupons.put("walmart", 15);
	storeCoupons.put("sears", 4);
	
	System.out.println(storeCoupons);

Output

{amazon=22, sears=4, walmart=15}

All the key and value entries of a map can be added to a different map using putAll method.

	Map<String, Integer> marks = new HashMap<String, Integer>();
	marks.put("a", 56);
	marks.put("d", 88);

	Map<String, Integer> marksTwo = new HashMap<String, Integer>();
	marks.put("b", 47);
	marks.put("c", 65);
	//put all object of a map
	marksTwo.putAll(marks);
	
	System.out.println(marksTwo);

Output

{a=56, b=47, c=65, d=88}

If you want to add a key and value pair to a map only if key doesn’t exist in the map, then you can use putIfAbsent method which returns current value if key exists otherwise it returns null.

	Map<String, Integer> storeCoupons = new HashMap<String, Integer>();
	storeCoupons.put("macys", 13);
	storeCoupons.put("nordstorm", 25);
	
	//add key value if the key doesn't exist in the map
	Integer val = storeCoupons.putIfAbsent("nordstorm", 33);
	
	if(val != null) {
		System.out.println("key exists with value: "+val);
	}
	
	System.out.println("map: "+storeCoupons);

Output

key exists with value: 25
map: {macys=13, nordstorm=25}

Getting Value from Map

Method get returns value from the map for the given key. If key is not present, it will return null value.

	System.out.println(storeCoupons.get("nordstorm"));
	System.out.println(storeCoupons.get("jcpenny"));

Output

25
null

To get a view of all keys present in the map as a set, method keySet is used.

		Map<String, Integer> storeCoupons = new HashMap<String, Integer>();
		storeCoupons.put("macys", 13);
		storeCoupons.put("nordstorm", 25);
		storeCoupons.put("amazon", 22);
		storeCoupons.put("walmart", 15);
		storeCoupons.put("sears", 4);
		
		Set<String> stores = storeCoupons.keySet();
		System.out.println(""+stores);

Output

[amazon, sears, macys, nordstorm, walmart]

To get all values of a map as collection view, method values() is used.

	Collection<Integer> couponCount = storeCoupons.values();
	System.out.println("map values: "+couponCount);

Output

map values: [22, 4, 13, 25, 15]

All the key and value pairs in a Map can be obtained as set view using entrySet() method, the set contains key and value pairs in the form of Map.Entry objects. The set reflects changes made to the Map and the Map reflects the changes made to the set. To get each Map.Entry object, you need to use iterator object of the set.

	Map<String, Integer> marks = new HashMap<String, Integer>();
	marks.put("a", 56);
	marks.put("d", 88);
	marks.put("b", 47);
	marks.put("c", 65);
	
	Set<Map.Entry<String, Integer>> marksEntries = marks.entrySet();
	Iterator<Map.Entry<String, Integer>> entryIterator = marksEntries.iterator();
	
	marks.put("b", 77);
	
	while(entryIterator.hasNext()) {
		Map.Entry<String, Integer> entry = entryIterator.next();
		System.out.println("key: "+entry.getKey()
								+" value: "+entry.getValue());
	}

Output

key: a value: 56
key: b value: 77
key: c value: 65
key: d value: 88

Map Remove and Replace Operations

Using remove method, you can remove a map entry for the given key. With the overloaded second version of remove method, you can remove an entry only if the given key is mapped to the specified value.

	Map<String, Integer> itemCountByBrand = new HashMap<String, Integer>();
	itemCountByBrand.put("samsung", 17);
	itemCountByBrand.put("sony", 14);
	itemCountByBrand.put("panasonic", 22);
	
	System.out.println("before remove: "+itemCountByBrand);
	
	itemCountByBrand.remove("samsung");
	itemCountByBrand.remove("sony", 14);
	itemCountByBrand.remove("panasonic", 12);
	
	System.out.println("after remove: "+itemCountByBrand);

Output

before remove: {samsung=17, sony=14, panasonic=22}
after remove: {panasonic=22}

Using replace method, a new value can be added to a key. Using second version of overloaded replace method, a value can be replaced only if existing value matches to the specified value.

	Map<String, Integer> itemCountByBrand = new HashMap<String, Integer>();
	itemCountByBrand.put("samsung", 22);
	itemCountByBrand.put("apple", 17);
	itemCountByBrand.put("panasonic", 19);
	
	System.out.println("before replace: "+itemCountByBrand);
	
	itemCountByBrand.replace("motorola", 29);
	itemCountByBrand.replace("samsung", 22, 35);
	itemCountByBrand.replace("panasonic", 12, 24);

Output

before replace: {apple=17, samsung=22, panasonic=19}
after replace: {apple=17, samsung=35, panasonic=19}

Using replaceAll method, you can replace value of each entry with the result of a function which is applied on each key and value pair.

	Map<String, Integer> brandTax = new HashMap<String, Integer>();
	
	brandTax.put("samsung", 22);
	brandTax.put("apple", 27);
	brandTax.put("panasonic", 18);
	
	System.out.println("before replaceAll: "+brandTax);
	
	brandTax.replaceAll( (k, v) -> {
		System.out.println("calculating new tax for "+k);
		return v*2/3;
	});
	
	System.out.println("after replaceAll: "+brandTax);

Output

before replaceAll: {apple=27, samsung=22, panasonic=18}
calculating new tax for apple
calculating new tax for samsung
calculating new tax for panasonic
after replaceAll: {apple=18, samsung=14, panasonic=12}

Map Merge Example

Using merge method, you can replace the value of a key with the result of the function supplied to it if the given key is mapped to a non null value.

	Map<String, String>  storeSupporedOffer= new HashMap<String, String>();
	storeSupporedOffer.put("walmart", "coupon, cashback");
	storeSupporedOffer.put("ebay", "deal");
	
	System.out.println("before merge: "+storeSupporedOffer);
	
	//key doesn't exist, it simply add entry with the key and value
	//this case bifunction is not executed.
	storeSupporedOffer.merge("amazon", "cashback", (k, v) -> {
		System.out.println("calculating new tax for "+k);
		return v + "deal, coupon";
	});
	
	System.out.println("after merge one: "+storeSupporedOffer);
	
	storeSupporedOffer.merge("ebay", "deal", (k, v) -> {
		System.out.println("calculating new tax for "+k);
		return v + ", cashback, coupon";
	});
	
	System.out.println("after merge two: "+storeSupporedOffer);	

Output

before merge: {walmart=coupon, cashback, ebay=deal}
after merge one: {amazon=cashback, walmart=coupon, cashback, ebay=deal}
calculating new tax for deal
after merge two: {amazon=cashback, walmart=coupon, 
cashback, ebay=deal, cashback, coupon}

Map ForEach Example

For each entry of the map, a function can be executed using forEach method. It takes BiConsumer as input.

	Map<String, String>  storeSupporedOffer= new HashMap<String, String>();
	storeSupporedOffer.put("walmart", "coupon, cashback");
	storeSupporedOffer.put("macys", "sale, cashback");
	storeSupporedOffer.put("ebay", "deal");
	
	storeSupporedOffer.forEach((k, v) -> {
		System.out.println("fetching "+v+" for "+k);
		//get offers from remote server
	});	

Output

fetching sale, cashback for macys
fetching coupon, cashback for walmart
fetching deal for ebay

Calculating Value using Compute

You can calculate a value for a key using compute operation.

	Map<String, Integer> brandTax = new HashMap<String, Integer>();		
	brandTax.put("samsung", 22);
	brandTax.put("apple", 27);
	
	System.out.println("map before compute: "+brandTax);
	
	Integer samsungNewTax = brandTax.compute("samsung",  (k, v) -> {
		System.out.println("calculating new tax for "+k);
		return v*2/3;
	});
			
	System.out.println("computed value: "+samsungNewTax);		
	System.out.println("map after compute: "+brandTax);

Output

map before compute: {apple=27, samsung=22}
calculating new tax for samsung
computed value: 14
map after compute: {apple=27, samsung=14}

Similarly, values can be computed using computeIfAbsent or computeIfPresent methods which calculate values when key is not mapped to value or key is mapped to a non null value respectively.

Map Stream Example

Following example shows how to use stream operations on keys set to perform certain actions on the entries of a map.

	Map<String, String> brandCat = new HashMap<String, String>();		
	brandCat.put("samsung", "electronics");	
	brandCat.put("levis", "jeans");	
	brandCat.put("nautica", "fashion");
	brandCat.put("adidas", "sports");

	brandCat.keySet().stream().map((s) -> {
		return s+"-"+brandCat.get(s);
	}).forEach((s) -> {
		System.out.println("getting products from remote services");
		System.out.println("for "+s);
	});

Output

getting products from remote services
for samsung-electronics
getting products from remote services
for levis-jeans
getting products from remote services
for adidas-sports
getting products from remote services
for nautica-fashion

TreeMap Examples

TreeMap is a navigable map. It sorts its keys based on natural order or using the comparator passed to its constructor. It provides navigation methods such as headMap, tailMap, lowerKey, higherKey, etc.

Following example shows that keys of tree map are order using natural order of the keys.

	TreeMap<Integer, String> players  = new TreeMap<>();
	players.put(7, "messi");
	players.put(2, "lakaku");
	players.put(21, "Ronaldo");
	players.put(5, "neymer");
	
	System.out.println("players map: "+players);

Output

players map: {2=lakaku, 5=neymer, 7=messi, 21=Ronaldo}

Following example shows passing comparator to tree map constructor to order keys based on it.

	//instantiate tree map by passing comparator to its constructor
	TreeMap<Integer, String> players  = new TreeMap<>((key1, key2) -> {
		if(key1 > key2) {
			return -1;
		}else {
			return 1;
		}
	});
	
	players.put(7, "messi");
	players.put(2, "lukaku");
	players.put(21, "Ronaldo");
	players.put(5, "neymer");
	
	System.out.println("players map: "+players);

Output

players map: {21=Ronaldo, 7=messi, 5=neymer, 2=lukaku}

If you want to get a key which is the least key among keys which are greater than or equal to the given key, you can use ceilingKey() method, similarly to get ceiling entry, you can use ceilingEntry().

You can get a key which is the greatest key among keys which are less than or equal to the given key, you can use floorKey() method, similarly to get floor entry, you can use floorEntry().

	TreeMap<Integer, String> players  = new TreeMap<>();
	
	players.put(7, "messi");
	players.put(23, "suarex");	
	players.put(2, "lakaku");
	players.put(21, "Ronaldo");
	players.put(5, "neymer");
	players.put(9, "pogba");
	players.put(13, "lukaku");
			
	System.out.println("map: "+players);
	System.out.println("cieling key: "+players.ceilingKey(12));
	System.out.println("cieling key: "+players.ceilingKey(13));
	System.out.println("floor key: "+players.floorKey(12));	
	System.out.println("floor key: "+players.floorKey(13));
	
	System.out.println("cieling entry: "+players.ceilingEntry(11));
	System.out.println("floor entry: "+players.floorEntry(11));

Output

map: {2=lakaku, 5=neymer, 7=messi, 9=pogba, 13=lukaku, 21=Ronaldo, 23=suarex}
cieling key: 13
cieling key: 13
floor key: 9
floor key: 13
cieling entry: 13=lukaku
floor entry: 9=pogba

If you want to get a key which is the least key among keys which are greater the given key, you can use higherKey() method, similarly to get higher entry, you can use higherEntry().

You can get a key which is the greatest key among keys which are less than the given key using lowerKey() method, similarly to get lower entry, you can use lowereEntry().

		System.out.println("map: "+players);
		System.out.println("higher key: "+players.higherKey(12));
		System.out.println("higher key: "+players.higherKey(13));
		System.out.println("lower key: "+players.lowerKey(12));	
		System.out.println("lower key: "+players.lowerKey(13));
		
		System.out.println("higher entry: "+players.higherEntry(11));
		System.out.println("lower entry: "+players.lowerEntry(11));

Output

map: {2=lakaku, 5=neymer, 7=messi, 9=pogba, 13=lukaku, 21=Ronaldo, 23=suarex}
higher key: 13
higher key: 21
lower key: 9
lower key: 9
higher entry: 13=lukaku
lower entry: 9=pogba

HeadMap TailMap SubMap Examples

To obtain a map, which is a view of portion of the main map, containing keys greater than or equal to the specified key, you can use tailMap() method. Similarly, to get a map, which is a view of portion of the main map, containing keys less than or equal to the specified key, you can use headMap() method.

To obtain a map, which is a view of portion of the main map, containing keys which are in between the given two keys, you can use subMap() method.

	TreeMap<Integer, String> players  = new TreeMap<>();
	
	players.put(7, "messi");
	players.put(23, "suarex");	
	players.put(2, "lakaku");
	players.put(21, "Ronaldo");
	players.put(5, "neymer");
	players.put(9, "pogba");
	players.put(13, "lukaku");
			
	System.out.println("map: "+players);
	SortedMap<Integer, String> tailMap = players.tailMap(8);
	System.out.println("tail map: "+tailMap);
	SortedMap<Integer, String> headMap = players.headMap(13);
	System.out.println("head map: "+headMap);
	SortedMap<Integer, String> subMap = players.subMap(4, 14);
	System.out.println("sub map: "+subMap);

Output

map: {2=lakaku, 5=neymer, 7=messi, 9=pogba, 13=lukaku, 21=Ronaldo, 23=suarex}
tail map: {9=pogba, 13=lukaku, 21=Ronaldo, 23=suarex}
head map: {2=lakaku, 5=neymer, 7=messi, 9=pogba}
sub map: {5=neymer, 7=messi, 9=pogba, 13=lukaku}

TreeMap Navigable Key Set

NavigableSet object containing all keys of TreeMap object can be obtained by calling navigableKeySet() method on the tree map object. Method descendingKeySet returns NavigableSet (view of keys exist in the map) containing keys in reverse order.

		System.out.println("map: "+players);		
		NavigableSet<Integer> navigableKeySet = players.navigableKeySet();
		System.out.println("navigable key set: "+navigableKeySet);
		
		System.out.println("first key: "+navigableKeySet.first());
		System.out.println("last key: "+navigableKeySet.last());
		System.out.println("higher key: "+navigableKeySet.higher(9));
		System.out.println("lower key: "+navigableKeySet.lower(9));

Output

navigable key set: [2, 5, 7, 9, 13, 21, 23]
first key: 2
last key: 23
higher key: 13
lower key: 7

Descending Map

TreeMap provides descendingMap method which returns reverse order view of the map.

	System.out.println("map: "+players);	
	System.out.println("map: "+players.descendingMap());

Output

map: {2=lakaku, 5=neymer, 7=messi, 9=pogba, 13=lukaku, 21=Ronaldo, 23=suarex}
map: {23=suarex, 21=Ronaldo, 13=lukaku, 9=pogba, 7=messi, 5=neymer, 2=lakaku}

LinkedHashMap

LinkedHashMap is a linked list implementation of map interface. It maintains the order of elements in which they are added to map.

	Map<String, Integer> offerCount = new HashMap<String, Integer>();		
	offerCount.put("deals", 22);
	offerCount.put("coupons", 53);
	offerCount.put("cashback", 33);
	
	System.out.println("hash map: "+offerCount);

	Map<String, Integer> offerCountTwo = new LinkedHashMap<String, Integer>();
	offerCountTwo.put("deals", 22);
	offerCountTwo.put("coupons", 53);
	offerCountTwo.put("cashback", 33);
	
	System.out.println("linked hash map: "+offerCountTwo);

Output

hash map: {coupons=53, deals=22, cashback=33}
linked hash map: {deals=22, coupons=53, cashback=33}

EnumMap Example

Key type of EnumMap is an enum type, meaning enum values should be used as keys of enum map. All the key values should be from a single enum.

	enum Offer {
		COUPON, CASHBACK, DEAL, SALE
	}

	public static void main(String[] args) {
		
		Map<Offer, Integer> offersCount = new EnumMap<Offer, Integer>(Offer.class);
		offersCount.put(Offer.CASHBACK, 20);
		offersCount.put(Offer.COUPON, 27);
		offersCount.put(Offer.DEAL, 10);
		
		System.out.println("Enum map: "+offersCount);
	}

Output

Enum map: {COUPON=27, CASHBACK=20, DEAL=10}

WeakHashMap

WeakHashMap is an implementation of Map interface with weak keys. Due to the weak reference to keys, key value pairs or entries in WeakHashMap will automatically be removed when the keys are no longer in ordinary use. If an object which is used as a key of a WeakHashMap is eligible for the garbage collection, when gc happens, entry for the key will be removed from the WeakHashMap. WeakHashMap is useful when you want to use map for implementing cache.

Following example shows that when there are no references to the keys for which there are entries in the HashMap and WeakHashMap, corresponding entries will be removed from the WeakHashMap but not from the HashMap after the garbage collection.

	String brand = new String("apple");
	String brandTwo = new String("samsung");
	
	Map<String, String> brandCat = new HashMap<String, String>();		
	brandCat.put(brand, "electronics");		
	
	Map<String, String> brandCatWeak = new WeakHashMap<String, String>();		
	brandCatWeak.put(brandTwo, "electronics");
	
	System.out.println("before garbage collection");
	System.out.println("hash map: "+brandCat);
	System.out.println("weak hash map: "+brandCatWeak);
	
	brand = null;
	brandTwo = null;
	System.gc();
	
	System.out.println("after garbage collection");
	System.out.println("hash map: "+brandCat);
	System.out.println("weak hash map: "+brandCatWeak);

Output

before garbage collection
hash map: {apple=electronics}
weak hash map: {samsung=electronics}
after garbage collection
hash map: {apple=electronics}
weak hash map: {}