HashMap
HashMap extends AbstractMap and implements Map Interface. HashMap is used when we want to store unordered and unsorted key value pairs where key is always unique.
HashMap is based on Hash Table data structure. HashMap uses hash table and bucketing logic to store each entries (key and value).
When you want to retrieve an object out of HashMap, you have to pass the key. Based on the hashcode of the provided key, the underlying algorithms locates the key and returns the associated value.
HashMap, like any other hash based collections, provides constant in time operations like put, get, remove, contains etc. Because, the logic is based on hashcode and the size of the HashMap doesn’t affect the time it requires.
HashMap is used to store object in key value pair where key is always unique.
key and value both are object type.
HashMap support null key and null value, However one and only one null key allowed.
Value may be duplicated, for a key. HashMap update latest value for given key.
By default entries in HashMap are unordered and unsorted while Iterating.
Iterators on the HashMap are fail-fast and throws ConcurrentModificationException when the HashMap is modified, while an iterator is active.
Hashmap operations are not synchronized and you will have to synchronoize the threads accessing HashMap on you own.
HashMap is based on Hash Table data structure. HashMap uses hash table and bucketing logic to store each entries (key and value).
When you want to retrieve an object out of HashMap, you have to pass the key. Based on the hashcode of the provided key, the underlying algorithms locates the key and returns the associated value.
HashMap, like any other hash based collections, provides constant in time operations like put, get, remove, contains etc. Because, the logic is based on hashcode and the size of the HashMap doesn’t affect the time it requires.
Feature of HashMap
Constructor of HashMap
HashMap() - Default HashMap constructor, with default capacity 16 and default loadFactor of 0.75
HashMap(Map<? extends K, ? extends V> map) - This Constructor is used to create Map based on some existing Map.
HashMap(int capacity) - This Constructor is used initialize HashMap with provided capacity and default load factor
HashMap(int capacity, double loadFactor) - this constructor is used to initialize HashMap with provided capacity and load factor.
HashMap(Map<? extends K, ? extends V> map) - This Constructor is used to create Map based on some existing Map.
HashMap(int capacity) - This Constructor is used initialize HashMap with provided capacity and default load factor
HashMap(int capacity, double loadFactor) - this constructor is used to initialize HashMap with provided capacity and load factor.
What is Capacity and Load Factor
Like any other Collection class HashMap also comes with default Capacity and default load Factor. An instance of HashMap depends upon two factor 1. Capacity and 2 . Load Factor.The Capacity is number of bucket in hash table. The HashMap has default capacity 16 and load factor 0.75. Which means when a HashMap is 75 % occupied the background process create a bigger HashMap based on some algorithm, and migrate all entries of old HashMap to new HashMap. Old HashMap become the candidate of garbage collector.
Example of HashMap
HashMap using Constructor
//Default Constructor default capacity and default load factor
Map<String,String> map=new HashMap<>();
map.put("country","India");
map.put("capital","Delhi");
This is most basic and most used form of HashMap creation. First you will create a HashMap using Default constructor, then using put(K k, V v) method add entries to Map. This is dyanmic and mutable map so you can put , replace or remove entries any number of times.
Create Immutable HashMap using Collections utility Class
Immutable Map once created can't be modified i.e. you cannot add, update, remove any entries. if you try you will get UnsupportedOperationException.
Map<String,String> map=new HashMap<>();
map.put("country","India");
map.put("capital","Delhi");
Map<String,String> immutableMap=Collections.unmodifiableMap(map);
you can create Immutable Map using unmodifiableMap() method of Collections utility class, it is two step process.
1. firstly you create a mutable map
2. then convert this mutable map using Collections class unmodifiableMap() static method.
This will create two physical Map which not good as production point of view.
Create Immutable HashMap using Java 9 of() and ofEntries() method
using Map.of() overloaded method, this method is overloaded upto 10 key value pair
for example:-
Map<String,String> immutableMap = Map.of("Country","India");
Map<String,String>immutableMap=Map.of("country","india","state","delhi");
Map.ofEntries() method not restricted like Map.of() method, it have varargs type of Map.Entry<? extends K, ? extends V>.
Map.entry() method is used to create Map.Entry type object.
Map<String,String> immutableMap=Map.ofEntries(Map.entry("country","India"),Map.entry("Capital","Delhi"));
Create Singleton and Empty HashMap using Collections
What is Singleton Hash Map
Singleton Hash Maps have single entry and immutable in nature, In other words you cannot add , update or remove another entry
Map<String,String> singletonMap=Collections.singletonMap("Country","India");
What is Empty Hash Map
Empty Hash Maps also immutable , you cannot add, update, remove it later. As name suggest you can create an empty Hash Map has no entry.
Map<String,String> emptyHashMap=Collections.emptyMap();
Using Stream to create HashMap
Java 8 Streams have Collectors.toMap() method to create Map from different things.
Convert List to Map using Stream Collectors.toMap method
Lets consider Book Class which have two member fields isbn and title and getters and setters and a constructor with both members.
public class Book{
String isbn;
String title;
//getter and setter
//constructor using parameter
}
List of Books
List<Book> bookList = new ArrayList<>();
bookList.add(new Book("isbn1","Java Tech"));
//
The Java Stream Collectors.toMap() is most popular method to create maps. This method take two Lambda expression, out of which first is key other is value.
Map<String,String> bookMap=bookList.stream().collect(Collectors.toMap(Book::getIsbn, Book::getTitle));
However you can create inline map using Collectors.toMap
Map<String,String>bookinlineMap=Stream.of(
new Book("isbn3","title3");
// more objects if required
).collect(Collectors.toMap(Book::getIsbn, Book::getTitle));
Book class method getIsbn and getTitle are non static method , however they are referred in static way Book::getIsbn and Book::getTitle . Because, when you iterate a collection, like the one above, the Java Stream will smartly replace the Class Name with the instance of current object. Hence the call actually happens on the nth Book in the list – where n is the index of current iteration.
Convert Entrie object to Map Value List<V> to Map<K,V> using Stream Collectors.toMap() method
In the above example only isbn and title member are used to create map entries. But in some situation you want to create Book object as value.
public Class Book{
private String isbn;
private String title;
private String author;
private double price;
// getter and setter
// public Book(String isbn,String title,String author, double price){
this.isbn=isbn;
this.title=title;
this.author=author;
this.price=price;
}
}
In this case we use following
Map<String,Book> map=bookList.stream().collect(Collectors.toMap(Book::getIsbn, Function.identity()));
Here, we have used
Function.identity()
to refer to the actual Book instance. Where the identity() method
returns a function that returns the input object.
How to group by a Field using Stream groupingBy
Many a times, we have to iterate through a Collection and group by id or any other field. To demonstrate we will iterate the above list of books to group by books with same name.List<bookList> ————> Map<String, List<Book>>
Map<String, List<Book>> groupedByName = bookList.stream().collect(Collectors.groupingBy(Book::getTitle));
Here, we have used
Collectors.groupingBy
and passed a Method Reference to users name.And, the output we get:
Using Guava Library to Create HashMap
Using Google’s Guava Library, you can create and initialize maps inline.Immutable Map using Google Guava
Map<String, String> map = ImmutableMap.of("country", "India", "capital", "Delhi", "Flag", "Tricolor");
However, it creates an Immutable map of n key/value pairs. The of function takes varargs and you can pass any number of entries inline.
There is a way to create a Mutable map using Google Guava.
Mutable Map using Google Guava
Map<String, String>ImmutableMap
=ImmutableMap.of("country", "India", "capital", "Delhi", "Flag", "Tricolor"
Map<String, String> mutuableMap = Maps.newHashMap(immutableMap);
However, it creates two different maps. Firstly create an immutable map with the entries and then create a mutable map.
1. Iterating Key of HashMap
- Via key Iterator using Set<K> keySet() method
- Via for-each loop
- Via a Stream
Via Key Iterator using Set<K> keySet() method
you can iterate all the key of Map using keySet() method, It return Set<K> from this we get iterator of the set following example demonstrate.
public static void main(String[] args) {
Map<String,String> map=new HashMap<>();
map.put("Country", "India");
map.put("Capital", "Delhi");
map.put("Population", "125M");
Iterator<String> iterator =map.keySet().iterator();
while(iterator.hasNext()) {
String key=iterator.next();
String value =map.get(key);
System.out.println(key +" :" +value);
}
}
//output
Country :India
Population :125M
Capital :Delhi
Via For-Each loop
from Java 5 you can use for each loop to iterate Map .
for example :
public static void main(String[] args) {
Map<String,String> map=new HashMap<>();
map.put("Country", "India");
map.put("Capital", "Delhi");
map.put("Population", "125M");
for(String key : map.keySet()) {
System.out.println(key +":"+map.get(key));
}
}
// output
Country :India
Population :125M
Capital :Delhi
Via Stream
From Java 8 you can use Java Stream to iterate key of the Map. First obtain keySet from Map then obtain stream from keySet .
Following example demonstrate this
public static void main(String[] args) {
Map<String,String> map=new HashMap<>();
map.put("Country", "India");
map.put("Capital", "Delhi");
map.put("Population", "125M");
Stream<String> stream=map.keySet().stream();
stream.forEach(key ->System.out.println(key+":"+map.get(key)));
//map.keySet().stream().forEach(System.out::println);
}
From Java 8 you can use Java Stream to iterate key of the Map. First obtain keySet from Map then obtain stream from keySet .
Following example demonstrate this
public static void main(String[] args) {
Map<String,String> map=new HashMap<>();
map.put("Country", "India");
map.put("Capital", "Delhi");
map.put("Population", "125M");
Stream<String> stream=map.keySet().stream();
stream.forEach(key ->System.out.println(key+":"+map.get(key)));
//map.keySet().stream().forEach(System.out::println);
}
// output
Country :India
Population :125M
Capital :Delhi
2. Iterating the Value of Map
- Via values() method
- via for-each
- via stream
public static void main(String[] args) {
Map<String,String> map=new HashMap<>();
map.put("Country", "India");
map.put("Capital", "Delhi");
map.put("Population", "125M");
/*
* Iterator<String> iterator =map.keySet().iterator(); while(iterator.hasNext())
* { String key=iterator.next(); String value =map.get(key);
* System.out.println(key +" :" +value); }
*/
/*
* for(String key : map.keySet()) { System.out.println(key +":"+map.get(key)); }
*/
/*
* Stream<String> stream=map.keySet().stream(); stream.forEach(key
* ->System.out.println(key+":"+map.get(key)));
*/
//Via Iterator
Iterator<String> value=map.values().iterator();
while(value.hasNext())
System.out.println(value.next());
//via For-each
for(String viaForEachValue :map.values()) {
System.out.println(viaForEachValue);
}
//Via Stream
map.values().stream().forEach(viaStreamValue->System.out.println(viaStreamValue));
}
3. Iterating Entries of HashMap
- Using Iterator
- Using for-each loop
- Using JAVA 8
Using Iterator
public class Student{
private int id;
private String firstName;
private String lastName;
private String zipCode;
//accessor and mutator methods
// constructor
......
}
List<Student> students=new ArrayList<>();
students.add(new Student(1 ,"Yogesh","Kumar","110010"));
students.add(new Student(2, "Mohan","P","111011"));
Map<Integer,Student> map=students.stream().collect(Collectors.toMap(Student::getId, Function.identity());
Iterator<Map.Entry<Integer,Student>> iterator = map.entrySet().iterator();
while(iterator.hasNext()){
Map.Entry<Integer,Student> mapEntry=iterator.next();
Integer key=mapEntry.getKey();
Student student=mapEntry.getValue();
}
Iterator<Map.Entry<Integer,Student>> iterator = map.entrySet().iterator();
while(iterator.hasNext()){
Map.Entry<Integer,Student> mapEntry=iterator.next();
Integer key=mapEntry.getKey();
Student student=mapEntry.getValue();
}
for(Map.Entry<Integer,Student> mapEntry : map.entrySet()){
Integer key=mapEntry.getKey();
Student student=mapEntry.getValue();
}
map.stream().forEach((k,v)->{System.out.println(k +":"+v)});
map.entrySet().forEach(System.out::println); //method reference;
HashMaps and Multi Threading.
Working with multi-threading is always tricky. But, when it comes to HashMaps along with multi threading, it is straightforward. However, you need to know few of the basics. Let’s cover those basics here.HashMaps are not synchronized. This means, you can actually have multiple threads reading and writing from same Hashmap.
For example, consider you have a thread that is iterating a HashMap of size 10. Meanwhile, another thread removes an element from the Hashmap and the size now became 9. This can cause the iteration logic go on toss. To make it easier the iterators are made fail-fast. In other words, when the iterator senses modification of the underlying HashMap, they immedietly throw ConcurrentModificationException.
This behavior is really helpful, as you can rely on the application to fail and hence no need to worry about having dirty reads. Although, if snychronization between threads is really important for you, you can still synchnronize the blocks or the objects accessing the HashMaps.
Alternatevly, you can use a Synchrnoized copy of your Hashmap. Refer to below example to learn how to get a synchronized copy of your HashMap.
- Map synchronizedMap = Collections.synchronizedMap(hashMap);
The synchronizedMap is a synchrnozed copy of your map. You can use this map safely with the threads. However, remember this is a copy of your existing hashmap. Therefore, if you have a really big hashmap this will be expensive on the memory.
When to use HashMaps
HashMaps have variety of usages. Having a key value structure they can be used to store many different type of elements. They are useful, when you do not have to worry about sorting or ordering.Consider, you have a properties file to read and keep in memory whenever you application wants to access any property. You can read the file once and store all they key value pairs in a HashMap and keep the map accessible to your code. Then your application can query the map with a specific key and access the associated value in constant amount of time.
Moreover, because of its structure it can be used to hold entire database table in a
List<Map<String, Object>>
. Where each map in the list represent entire row of a table. Similarly, it can also used to generically hold entire request body in a web application.Additionally, in below example we will create an User instance and map it to a HashMap using fasterxml library.
- User user = new User(1L, "Arya", "Stark", 14);
- ObjectMapper objectMapper = new ObjectMapper();
- // Covert object to a Map
- Map<String, Object> objectMap = objectMapper.convertValue(user, Map.class);
- System.out.println(objectMap);
- // Output:
- // {id=1, name=Arya, lastName=Stark, age=14}
No comments:
Post a Comment