Thursday 26 November 2015

Why Key of a Map must be Immutable ?

Immutable Object :
An immutable object is a special kind of object whose state can not be changed once it is created. These objects are used in concurrent applications as the states can not be changed, So there is no question of corrupting the state by thread interference. And one more major use is while working with java.util.Map the key must be an immutable object. Else once you have added one entry to the map and after that if the state of the key got changed then you will lose the object mapping.

Example:
class User {
    String email;
    String name;
    public User(String email, String name) {
        this.email = email;
        this.name = name;
    }
    public String getEmail() {
        return email;
    }
    public void setEmail(String email) {
        this.email = email;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    @Override    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        User user = (User) o;
        if (!email.equals(user.email)) return false;
        return name.equals(user.name);
    }
    @Override    public int hashCode() {
        int result = email.hashCode();
        result = 31 * result + name.hashCode();
        return result;
    }
}
public class ImmutableKeyTest {
    public static void main(String[] args) {
        Map<User, String> userDealMap = new HashMap<>();
        User user = new User("abc@gmail.com", "ABC");
        User user1 = new User("xyz@gmail.com", "XYZ");
        userDealMap.put(user, "HAPPY50");
        userDealMap.put(user1, "HAPPY25");
        System.out.println(userDealMap);

        System.out.println("Before Key Change");
        System.out.println(userDealMap.get(user));
        user.setName("ABC DEF");
        System.out.println("After Key Change");
        System.out.println(userDealMap.get(user));
    }
}
Output: {User@b42dca6f=HAPPY25, User@4a84e2cf=HAPPY50} Before Key Change HAPPY50 After Key Change null Here when the name of the user got changed, it impacts the hashCode of the user object, so while getting the value from the map, it treats the supplied key as a new key and returns NULL.

Solution: Solution is very simple ! Make the User object as immutable object. (Guideline for creating immutable object refer https://docs.oracle.com/javase/tutorial/essential/concurrency/imstrat.html)

final class User {
    final String email;
    final String name;
    public User(String email, String name) {
        this.email = email;
        this.name = name;
    }
    public String getEmail() {
        return email;
    }
    public String getName() {
        return name;
    }
    @Override    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        User user = (User) o;
        if (!email.equals(user.email)) return false;
        return name.equals(user.name);
    }
    @Override    public int hashCode() {
        int result = email.hashCode();
        result = 31 * result + name.hashCode();
        return result;
    }
}
Once you make it immutable, you can not change the state of the object by any chance.
// user.setName("ABC DEF"); // This will give compilation error as no setter method there

Tuesday 24 November 2015

Know LinkedHashMap in Depth

LinkedHashMap is a map which extends HashMap class and implements Map interface. But the only different between LinkedHashMap and HashMap is LinkedHashMap maintains the insertion order whereas HashMap doesn't. LinkedHashMap uses double LinkedList internally to maintain the order.

Example :

class Employee{
    String id;
    String name;
    public Employee(String id, String name) {
        this.id = id;
        this.name = name;
    }
    @Override    public String toString() {
        return "["+id+","+name+"]";
    }
}

import java.util.HashMap;
import java.util.LinkedHashMap;

public class LinkedHashMapDemo1 {
    public static void main(String[] args) {
        LinkedHashMap<Employee, Integer> linkedMap = new LinkedHashMap<>();
        HashMap<Employee, Integer> hashMap = new HashMap<>();

        System.out.println("LinkedHashMap : ");
        linkedMap.put(new Employee("1001","Alpha"), 2000);
        linkedMap.put(new Employee("1002","Beta"), 2000);
        linkedMap.put(new Employee("1003","Gama"), 2000);
        System.out.println(linkedMap);

        System.out.println("HashMap : ");
        hashMap.put(new Employee("1001","Alpha"), 2000);
        hashMap.put(new Employee("1002","Beta"), 2000);
        hashMap.put(new Employee("1003","Gama"), 2000);
        System.out.println(hashMap);
    }
}
Output:
LinkedHashMap : 
{[1001,Alpha]=2000, [1002,Beta]=2000, [1003,Gama]=2000}
HashMap : 
{[1001,Alpha]=2000, [1003,Gama]=2000, [1002,Beta]=2000}

Here LinekedHashMap keeps the insertion order but HashMap doesn't.

Now the Secret of LinkedHashMap :

LinkedHashMap provided a special constructor to maintain access order instead insertion order. public LinkedHashMap(int initialCapacity, float loadFactor, boolean accessOrder) Access Order is like maintaining the order of the entries accessed. Access Order is affected by get(key), put(key,val) and putAll(Map). Because of this nature, LinkedHashMap is used in LRU(Least Recent Used) Caching.
Example: 
import java.util.LinkedHashMap;
import java.util.Map;
public class LinkedHashMapTest {
    public static void main(String[] args) {
        LinkedHashMap<String, Integer> linkedMap = new LinkedHashMap<>();
        System.out.println("Default LinkedHashMap : ");
        linkedMap.put("Gama", 2000);
        linkedMap.put("Alpha", 2000);
        linkedMap.put("Beta", 2000);
        // Once Added after that no change in order
        linkedMap.put("Alpha", 3000);
        linkedMap.put("Beta", 3000);
        linkedMap.put("Gama", 3000);
        linkedMap.get("Alpha");
        linkedMap.get("Beta");
        System.out.println(linkedMap);

        // LinkedHashMap with accessOrder        
        System.out.println("LinkedHashMap with accessOrder  = TRUE: ");
        LinkedHashMap<String, Integer> linkedAccessedOrderMap = 
                        new LinkedHashMap<>(10, 0.5f, true);
        linkedAccessedOrderMap.put("Gama", 2000);
        linkedAccessedOrderMap.put("Alpha", 2000);
        linkedAccessedOrderMap.put("Beta", 2000);
        linkedAccessedOrderMap.put("Alpha", 3000);
        linkedAccessedOrderMap.get("Gama");
        System.out.println(linkedAccessedOrderMap);
    }
}
Output:
Default LinkedHashMap : 
{Gama=3000, Alpha=3000, Beta=3000}
LinkedHashMap with accessOrder  = TRUE: 
{Beta=2000, Alpha=3000, Gama=2000}

Monday 23 November 2015

Secrets of java.lang.ThreadLocal - Part I

java.lang.ThreadLocal is given by Java API to achieve the thread-safety in multi-threaded environment. Unlike of synchronization or locking this eliminates the sharing concept rather maintains an individual copy of object for each thread. So no need of locking or synchronization which results more scalability and performance.


Output:
Here id and threadLocal both are shared resources but ThreadLocal maintains individual copy for each thread.

Now the Secret of ThreadLocal is
- Each Thread object contains an instance member variable as ThreadLocal.ThreadLocalMap threadLocals;
- Where as ThreadLocal.ThreadLocalMap contains array of entries of java.lang.ref.Reference type as private Entry[] table;
And this Entry is a simple <key, value> pair like Map.Entry which contains ThreadLocal<?> as key and Object as value
- ThreadLoacl class has 2 important methods 
public void set(T value) : while setting the value, it first gets the ThreadLocalMap of the current thread. Then adds an entry with <this ThreadLocal object, passed Value>
* public T get() : while getting the value, first gets the ThreadLocalMap of the current thread. Then get the value for the Entry by passing key as this current ThreadLocal object. If not found any value then sets and returns the initial value provided by the programmer. 
ThreadLocal class having a callback method to provide the initial value as protected T initialValue(); ]

To provide initial value we can override callback method as below :
ThreadLocal<Integer> threadLocal = new ThreadLocal<Integer>() {
    @Override    protected Integer initialValue() {
        return 100;
    }
};






Sunday 22 November 2015

Use Non-final variable inside Anonymous Inner Class

Accessing local variable inside an inner class need to be declared as final or effectively final(this is new in Java 8). 

But if we have a requirement of changing that variable after inner class then we can't declare the local variable as final. So to use that non-final local variable effectively inside inner class we can pass that variable as constructor argument to that inner class and use it.

Example : 

class IdGenerator {
    long id = 99990000;

    public IdGenerator() {
    }

    public String generateId() {
        return String.valueOf(++id);
    }
}

interface Service {
    void logData();
}
public class InnerClassWithNonFinalVariable {
    public static void main(String[] args) {
        IdGenerator idGenerator = new IdGenerator();

        class MyService implements Service {
            private IdGenerator generator;

            MyService(IdGenerator generator) {
                this.generator = generator;
            }

            @Override            public void logData() {
                System.out.println("Logging Data :" + this.generator.generateId());
            }
        }

        Service service = new MyService(idGenerator);
        service.logData();

        /* Using idGenerator for some other purpose */        idGenerator = null;
    }
}

But while working with Anonymous Inner Class, We don't have option to define constructor. So below demonstrated code shows how to use non-final local variable inside anonymous inner class.



Saturday 7 November 2015

Use Varargs Carefully !

So, what are the things need to take care while using varargs in Java ?
Let's take a simple example : 
public static int findMax(int... numbers) {
    int max = numbers[0];
   
for (int i = 1; i < numbers.length; i++) {
       
if (numbers[i] > max)
            max = numbers[i];
    }
   
return max;
}
findMax(1, 2, 3, 4, 5); // 5
findMax(10, 2, 30, 4, 50, 6); // 50
findMax(3); // 3

Here this above code looks good and the findMax() method works fine. But the problem here is in run time it will fail with specific inputs like :
findMax(null); // Exception in thread "main" java.lang.NullPointerException
findMax(); // Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 0

Because here the code expects at least one value.
So to avoid these problems we can modify the method in such way like :
public static int findMax(int... numbers) {
    if (numbers == null || numbers.length == 0)
       
throw new IllegalArgumentException("At least one input required.");
   
int max = numbers[0];
   
for (int i = 1; i < numbers.length; i++) {
       
if (numbers[i] > max)
            max = numbers[i];
    }
   
return max;
}

But here we need extra code:
#1. one more validation required
#2. the calling method need to be more cautious to handle "IllegalArgumentException"
Instead of all above resolution we can handle this problem in a shorter and smarter way by using the varargs in proper way.

public static int findMax(int firtNumber, int... numbers) {
   
int max = firtNumber;
   
for (int i = 0; i < numbers.length; i++) {
       
if (numbers[i] > max)
            max = numbers[i];
    }
    
return max;
}
Here we have made the first number as a separate parameter, So the compiler will not allow to invoke method by passing null or empty arguments like  findMax(null) and findMax().