Showing posts with label map. Show all posts
Showing posts with label map. Show all posts

Thursday, 23 June 2011

Type safety in Java Set and Map in JDK 1.4

Probably many of you still remember the lack of type checking in Java 1.4 Collections and how much hassle it was to deal with casting the collection elements, not to mention how many errors this introduced to the code. Since introduction of generics in Java 1.5 this have really improved and one might think that nowadays the language itself protects the programmer from the silliest of typing mistakes. Generics themselves brought with them a set of new complications, but it seems reasonable to think that in the basic code situations when using Java’s Sets or Maps and when there is no kung-fu complications like wildcards or casting we are safe and secure. Are we really?
Recently I have encountered a bug in a production code when dealing with a simple (really simple) usage of a Map. The embarassing part of this issue was that it took lots of effort to debug it and the cause of it was… well… trivial. The code below shows a sketch of how the code looked like before the bug was introduced. In real life the class was a part of a really old and rarely edited legacy code, obviously it was much larger than the one below:

import java.util.HashMap;
import java.util.Map;

public class EmployeeDataLookup {
// A map storing relation between employee ID and name.
private Map<Integer, String> employeeIdToName;

public EmployeeDataLookup() {
// Create a new EmployeeDataLookup and initialize
// it with employee names and IDs.
employeeIdToName = new HashMap<Integer, String>();
addEmployee(301, "John Doe");
addEmployee(302, "Mary Poppins");
addEmployee(303, "Andy Stevens");
}

public void addEmployee(int employeeId, String employeeName) {
employeeIdToName.put(employeeId, employeeName);
}

// This class is very complicated and has many other methods...

// Lookup method for finding employee name for given employee ID.
public String findEmployeeName(int employeeId) {
return employeeIdToName.get(employeeId);
}

public static void main(String[] args) {
// Create a EmployeeDataLookup instance
EmployeeDataLookup employeeLookup = new EmployeeDataLookup();
// Find the name of an employee with ID = 301
String employeeName = employeeLookup.findEmployeeName(301);
System.out.print("Employee 301 : " + employeeName);
}
}

So what went wrong? Well, at some point the project functionality requirements have changed (surprising, right?) and the key of the map storing data inside of the class had to be changed. In terms of the example above you might say that a company has merged with one of the vendors, so the system storing the employee data had to be made compatible with the ID system of the vendor. Because the vendor company used 13 digit IDs to identify its employees the change to the code in Java terms meant that instead of using Integers we had to change to Longs. Simple right? Its even easier if you use an IDE like Eclipse – just change/refactor the Map key type in line 7 from Integer to Long, let the editor show you all the errors, fix them and that is it! In five minutes all is done:

import java.util.HashMap;
import java.util.Map;

public class EmployeeDataLookup2 {
// A map storing relation between employee ID and name.
private Map<Long, String> employeeIdToName;

public EmployeeDataLookup2() {
// Create a new EmployeeDataLookup and initialize
// it with employee names and IDs.
employeeIdToName = new HashMap<Long, String>();
addEmployee(301, "John Doe");
addEmployee(302, "Mary Poppins");
addEmployee(303, "Andy Stevens");
}

public void addEmployee(long employeeId, String employeeName) {
employeeIdToName.put(employeeId, employeeName);
}

// This class is very complicated and has many other methods...

// Lookup method for finding employee name for given employee ID.
public String findEmployeeName(int employeeId) {
return employeeIdToName.get(employeeId);
}

public static void main(String[] args) {
// Create a EmployeeDataLookup instance
EmployeeDataLookup employeeLookup = new EmployeeDataLookup();
// Find the name of an employee with ID = 301
String employeeName = employeeLookup.findEmployeeName(301);
System.out.print("Employee 301 : " + employeeName);
}
}

The change above introduced the bug. Can you see it? When you run the main method now you will find out that the name of the employee with ID=301 is ‘null’! Ha!

If you do not see the trouble (or are to lazy to try) I’ll give you a hint: check out the function findEmployeeName . If you have seen it, try to imagine that this class in real life had 1000+ lines of code unrelated to the changed map. Since there are no compile errors it was like looking for a needle in a haystack… so what’s the bug exactly?

The problem is that after introducing generics, probably due to backward compatibility, some of the methods in Collection and Map interface have not been changed and still do not perform type checking. One of them is Map.get(Object) which have caused the bug in the code above. After the ’simple change’ we have still been using Integer keys to access the map that contained Longs, so even though we had a record of employee 301 in our system the get method returned null. And because of the lack of type checking there were no warnings… Busted.

So what’s the lesson from this? Be cautious about type checking even in the most basic situations. Remember that access methods in Sets, Lists and Maps can behave and bite you in the behind. The biggest pain is that the bugs introduced this way are usually hard to spot by a human, so sometimes a dozen of engineers staring at the code can have trouble finding it. There is a way to deal with them though – most of them can be easily found if you are using a static code analysis tool (eg: FindBugs).

Note:
If you are using JDK 6 or higher, its no worries for you. Because in any case you would get the correct result.

Tuesday, 14 June 2011

How to convert map to List in java?

Assuming map is your instance of Map :
map.values() will return a Collection containing all of the map's values
map.keys() will return a Set containing all of the map's keys
map.entrySet() will return a Set containing all map key value pairs.

Get list of keys

List<String> list = new ArrayList<String>(map.keySet());

Get list of values

List<String> list = new ArrayList<String>(map.values());

Saturday, 28 May 2011

The Properties class in java

The Properties class inherits from Hashtable and is part of JDK 1.0. Instances of Properties typically represent persistent values describing something such as system settings. For example, the System.getProperties() method returns a system properties table with various key/value pairs that describe the platform the program is running on.

The Properties class adds additional functionality that can be extremely useful. The Properties class represents a persistent list of properties. What exactly does this mean? The Properties class is designed to provide methods to store and load files containing key-value pairs as Strings. Properties files are simply key-value pairs where the key and value are separated by an "=" (name=Tom) and eack key-value pair is on a separate line. A Windows ini file is an example of a properties file. In many cases, XML files are replacing properties files but the simplicity of a properties file combined with its ease of use makes the Properties class still a very useful class.

Although the Properties file inherits the methods of the Hashtable, it is recommended that the Hashtable put method not be used as this may allow the insertion of non-String objects. Only Strings should be used as either a key or a value in a Properties object. A Properties object with a non-String in it will throw an exception if you attempt to store it to a file. A Properties object can contain another Properties object (specified in the constructor) which is used as the default keys if no matching key is found in the Properties object.

Methods in Property class

The Properties class provides several new methods:

  • Object setProperty(String key, String value) - Invokes the Hashtable put method. Although it returns an Object, the return should always be a String.
  • String getProperty(String key) - Returns the value found for the matching key. Null if no match is found.
  • String getProperty(String key, String defaultValue) - Returns the value found for the matching key.The defaultValue is returned if no match is found.
  • void list(PrintStream output) - Writes the Properties object out to the specified PrintStream.
  • void list(PrintWriter output) - Writes the Properties object out to the specified PrintWriter.
  • void load(InputStream input) - Loads data into the Properties object using the specified InputStream.Key-value pairs are assumed to be on separate lines and each kei is separated from its value by an equals sign ("="). In addition to the equals sign, a colon (":") or the first white space will be used as a separator when reading in a properties file.
  • void store(OutputStream output, String header) - Writes the Properties object out to the specified OutputStream. The header is written as a comment line at the beginning of the file. After the header line, another comment line is written containing the current date and time. A "#" is placed at the beginning of these lines to identify them as comments. The OutputStream will be flushed but will remain open. Any properties in the default Properties object are not written to the output.

Example of Properties class in java

Property class example in java

Here is an example of reading in a properties file, removing the password and printing out the reminder.
import java.util.*;
import java.io.*;

public class PropertyDemo {

public static void main(String[] args)
throws Exception {

BufferedInputStream bStream = new BufferedInputStream
(new FileInputStream("prefs.ini"));
Properties props = new Properties();
props.load(bStream);
props.remove("password");
props.list(System.out);
bStream.close();
}
}

LinkedHashMap in java

New to J2SE 1.4, the LinkedHashMap class is used in exactly the same way as the HashMap class. It has no additional methods but it does have one additional constructor that we will discuss in a moment. The basic difference between HashMap and LinkedHashMap is that the LinkedHashMap maintains the order of the items added to the Map. It does this by maintaining a doubly linked list containing the hash and the original order of the items. According to Sun, the LinkedHashMap should run nearly as fast as the HashMap.

The LinkedHashMap has one additional constructor that takes an additional boolean parameter. This allows you to construct a LinkedHashMap that maintains items, not in the order that they are added to the Map but rather in the order in which they are accessed.

Example of LinkedHashMap

LinkedHashMap example in java

This example shows LinkedHashMap in java:

public class JavaLinkedHashMapExample {
public static void main(String[] args) {
//create object of LinkedHashMap
LinkedHashMap lHashMap = new LinkedHashMap();
//put the values
lHashMap.put("One", new Integer(1));
lHashMap.put("Two", new Integer(2));
//get the value
Object obj = lHashMap.get("One");
System.out.println(obj);
//iterate over linked hash map values
Collection c = lHashMap.values();

//obtain an Iterator for Collection
Iterator itr = c.iterator();



//iterate through LinkedHashMap values iterator
while(itr.hasNext())
System.out.println(itr.next());
}
}

The IdenitityHashMap Class in java

The IdentityHashMap is also like the HashMap with the exception that a key is considered equal to another key only if they are pointing to the exact same object. Other implementations of Map use the equals( ) method to determine if two keys are equal. The IdentityHashMap uses the == comparator. Two keys (a and b) are considered equal if a == b. The IdentityhashMap should only be used in the cases where this functionality is required. It should not be used otherwise.

HashTable in java

Hashtable was part of the original java.util and is a concrete implementation of a Dictionary. However, Java 2 reengineered Hashtable so that it also implements the Map interface. Thus, Hashtable is now integrated into the collections framework. It is similar to HashMap, but is synchronized.

Like HashMap, Hashtable stores key/value pairs in a hash table. When using a Hashtable, you specify an object that is used as a key, and the value that you want linked to that key. The key is then hashed, and the resulting hash code is used as the index at which the value is stored within the table.

A hash table can only store objects that override the hashCode() and equals() methods that are defined by Object. The hashCode() method must compute and return the hash code for the object. Of course, equals() compares two objects. Fortunately, many of Java's built-in classes already implement the hashCode() method. For example, the most common type of Hashtable uses a String object as the key. String implements both hashCode() and equals().

Constructors of HashTable class

Hashtable( )
Hashtable(int size)
Hashtable(int size, float fillRatio)
Hashtable(Map m)

 

The first version is the default constructor. The second version creates a hash table that has an initial size specified by size. The third version creates a hash table that has an initial size specified by size and a fill ratio specified by fillRatio. This ratio must be between 0.0 and 1.0, and it determines how full the hash table can be before it is resized upward. Specifically, when the number of elements is greater than the capacity of the hash table multiplied by its fill ratio, the hash table is expanded. If you do not specify a fill ratio, then 0.75 is used. Finally, the fourth version creates a hash table that is initialized with the elements in m. The capacity of the hash table is set to twice the number of elements in m. The default load factor of 0.75 is used. The fourth constructor was added by Java 2.

 

Examples on HashTable


Friday, 27 May 2011

TreeMap in Java Collections

The TreeMap class guarantees that the keys in the Map will be sorted in ascending order by either the keys natural order or by a Comparator provided to the constructor of the TreeMap. TreeMap implements Map and SortedSet interfaces and extends AbstractMap. TreeMap keeps the balanced binary tree in sorted order by key.

Searching for an item in a TreeMap will be slower than in a HashMap because the hashing algorithm gives better performance than the compareTo() method which is used to locate items in a TreeMap.

 

Creating a TreeMap – Constructors for TreeMap

It has the following constructors. Here comp stands for Comparator, mp stands for Map, smp stands for SortedMap.

Result Constructor Description
tmap = new TreeMap() Creates new TreeMap. Keys sorted by natural order.
tmap = new TreeMap(comp) Creates new TreeMap using Comparator comp to sort keys.
tmap = new TreeMap(mp) Creates new TreeMap from Map mp using natural ordering.
tmap = new TreeMap(smp) Creates new TreeMap from SortedMap smp using key ordering from smp.


 

Methods in TreeMap

TreeMap adds the methods from Map and SortedMap. See the methods of these interfaces – Methods in Map interface, Methods in SortedMap interface.

So other than Map interface methods it has SortedMap interface Methods:

  • Object firstKey() - Gets the first key from the sorted Map.
  • Object lastKey() - Gets the last key from the sorted map.
  • SortedMap headMap(Object toKey) - Returns a view of the portion of this Map whose keys are less than toKey.
  • SortedMap tailMap(Object fromKey) - Returns a view of the portion of this Map whose keys are greater than or equal to fromKey.
  • SortedMap subMap(Object fromKey, Object toKey) - Returns a view of the portion of this Map whose starting key is greater than or equal to fromKey and whose ending key is less than toKey.

 

Example

TreeMap Example in java

Hashmap Tutorial

The HashMap class is the simplest implementation of the Map interface. The HashMap does not add any additional methods (other than clone) beyond those found in the Map interface.

Creating the HashMap in java

In addition to implemented the Map interface methods, HashMap has the following constructors.
Result Constructor Description
hmap = new HashMap() Creates a new HashMap with default initial capacity 16 and load factor 0.75.
hmap = new HashMap(initialCapacity) Creates a new HashMap with the specified initial int capacity.
hmap = new HashMap(initialCapacity, loadFactor) Creates a new HashMap with the specified capacity which will not exceed a specified (float) load factor.
hmap = new HashMap(mp) Creates a new HashMap with elements from the Map mp

 

Performance of the HashMap

The HashMap achieves good performance by using a hash to store the key in the Map. The hash allows fast lookup which means that the containKey( ) method will perform much better than the containsValue( ) method.
HashMaps will automatically grow when you add too many elements. However, growing requires copying, rehashing and rechaining, which affects its overall performance.
Performance of HashMap depends on two important factors that are
  • Initial Capacity and
  • Load Factor
Initial Capacity is the capacity at the time the HashMap is created. Load factor determines when to increase the capacity of the HashMap. The default load factor is 0.75.
Important Note: The initial capacity is not the actual number of elements you plan to store in HashMap. Say for example, if you set initial capacity of 100 and the load factor is 0.75, then the capacity of HashMap will be automatically increased when it reaches to 75 not 100.
Any Object used as a key in a HashMap must implement the hashCode( ) and equals( ) methods. See Part 2 of this series for a discussion of this issue.
The HashMap does not guarantee the order of the items in the Map and allows one null key. Duplicates are not permitted. The HashSet offers "constant time" performance for lookups involving the key and linear time for lookups based on value. This means that adding items to the Map will not cause significant performance degradation as long as lookups are done by the key. The performance of basic functions such as put, remove, get, etc is based on two factors which can be specified in the constructor of the HashMap, initial capacity and load factor.


Methods in HashMap

It inherits all the methods from Map and adds following method.
  • Object clone(): Returns a copy of the HashMap. The cloned object contains the same key/value pairs.
 So see – Map interface methods.

Example

Creating a HashMap in Java
Iterating over hashmap
Sorting a hashmap

Introduction to maps in java

A Map(in the API reference documentation)is an object that maps keys to values. A map cannot contain duplicate keys: Each key can map to at most one value.

These are the interfaces available for maps in java:

  • Map implemented by HashMap and TreeMap
  • SortedMap implemented by TreeMap.
  • Map.Entry which describes access methods to the key-value pairs.

Collection views for Maps for iteration

The Collection-view methods allow a Map to be viewed as a Collection in three ways:
  • keySet: the Set of keys contained in the Map.
  • values: The Collection of values contained in the Map. This Collection is not a Set, as multiple keys can map to the same value.
  • entrySet: The Set of key-value pairs contained in the Map. The Map interface provides a small nested interface called Map.Entry that is the type of the elements in this Set.

The Collection-views provide the only means to iterate over a Map.

Iterating over the keys in a Map

Here's an example illustrating the standard idiom for iterating over the keys in a Map:

for (Iterator i=m.keySet().iterator(); i.hasNext(); )
System.out.println(i.next());


Iterating over the values in Map

The idiom for iterating over values is analogous.

for (Iterator i=m.values().iterator(); i.hasNext(); )
System.out.println(i.next());


Iterating over the key value pairs

 

Here's the idiom for iterating over key-value pairs:

for (Iterator i=m.entrySet().iterator(); i.hasNext(); ) {
Map.Entry e = (Map.Entry) i.next();
System.out.println(e.getKey() +
": " + e.getValue());
}

When first presented with these idioms, many people worry that they may be slow because the Map has to create a new Collection object each time a Collection-view operation is called. Rest easy: This is not the case. There's no reason that a Map can't always return the same object each time it is asked for a given Collection-view. This is precisely what all of the JDK's Map implementations do.
With all three Collection-views, calling an Iterator's remove operation removes the associated entry from the backing Map (assuming the Map supports element removal to begin with). With the entrySet view, it is also possible to change the value associated with a key, by calling a Map.Entry's setValue method during iteration (again, assuming the Map supports value modification to begin with). Note that these are the only safe ways to modify a Map during iteration; the behavior is unspecified if the underlying Map is modified in any other way while the iteration is in progress.
The Collection-views support element removal in all its many forms: the remove, removeAll, retainAll, and clear operations, as well as the Iterator.remove operation. (Yet again, this assumes that the backing Map supports element removal.)
The Collection-views do not support element addition under any circumstances. It would make no sense for the keySet and values views, and it's unnecessary for the entrySet view, as the backing Map's put and putAll provide the same functionality.


SortedMap interface methods (java)

The SortedMap interface is used by TreeMap and adds additional methods to reflect that a TreeMap is sorted.
ResultMethodDescription
comp = comparator() Returns Comparator used to compare keys. null if natural ordering used (eg, String).
obj = firstKey() Key of first (in sorted order) element.
obj = lastKey() Key of last (in sorted order) element.
smp = headMap(obj) Returns SortedMap of all elements less than obj.
smp = tailMap(obj) Returns SortedMap of all elements greater than or equal to obj.
smp = subMap(fromKey, toKey) Returns SortedMap of all elements greater than or equal to fromKey and less than toKey.

Map.Entry interface methods (java)

Each element is a map has a key and value. Each key-value pair is saved in a java.util.Map.Entry object. A set of these map entries can be obtained by calling a map's entrySet() method. Iterating over a map is done by iterating over this set.
Assume in the following table that me is a Map.Entry object.
ResultMethod Description
obj = me.getKey() Returns the key from the pair.
objme.getValue(key) Returns the value from the Map pair.
objme.setValue(val) This is an optional operation and may not be supported by all Map.Entry objects. Sets the value of the pair, which modifies the Map which it belongs to. Returns the orginal value.

Map interface methods (java)


Here are some of the most useful Map methods. m is a Map, b is a boolean, i is an int, set is a Set, col is a Collection, key is an Object used as the key used to store a value, val is an Object stored as the value associated with the key.
ResultMethod Description
Adding key-value pairs to a map
obj = m.put(key, val) Creates mapping from key to val. It returns the previous value (or null) associated with the key.

m.putAll(map2) Adds all key-value entries from another map, map2.
Removing key-value pairs from a map

m.clear() Removes all elements from a Map
objm.remove(key) Deletes mapping from key to anything. Returns previous value (or null) associated with the key.
Retrieving information from the map
bm.containsKey(key) Returns true if m contains a key key
bm.containsValue(val) Returns true if m contains val as one of the values
objm.get(key) Returns value corresponding to key, or null if there is no mapping. If null has been stored as a value, use containsKey to see if there is a mapping.
bm.isEmpty() Returns true if m contains no mappings.
im.size() Returns number of mappings in m.
Retrieving all keys, values, or key-value pairs (necessary for iteration)
setm.entrySet() Returns set of Map.Entry values for all mappings.
setm.keySet() Returns Set of keys.
colm.values() Returns a Collection view of the values in m.

Map implementations in java

A number of classes implement the Map interface, including HashMap, TreeMap, LinkedHashMap, WeakHashMap, ConcurrentHashMap, and Properties. The most generally useful class is HashMap.
  • java.util.HashMap is implemented with a hash table. Access time is O(1). Entries are unsorted.
    – similar to HashTable but allows null keys and values
    – not thread safe
  • java.util.LinkedHashMap is implemented with a hash table. Access time is O(1). Entries are sorted in either entry order or order of last access, which is useful for implementing a LRU (least recently used) caching policy.
  • java.util.TreeMap is implemented as a balanced binary tree. Access time is O(log N). Entries are sorted. 
  • java.util.HashTable(old classes)
    – updated class from earlier Java versions
    – does not allow null keys or values
    – thread safe
  • java.util.WeakHashMap -
    – like HashMap
    – entry is automatically removed from HashMap if no more "ordinary" references to key
  • java.util.IdentityHashMap
  • java.util.EnumMap
  • java.util.Properties

Monday, 23 May 2011

ConcurrentSkipListMap Class in java Collections

ConcurrentSkipListMap is one of the class which implements NavigableMap.
This class implement all of the optional methods of the Map and Iterator interfaces. Like most other concurrent collections, this class does not permit the use of null keys or values because some null return values cannot be reliably distinguished from the absence of elements. NavigableMap class is similar to NavigableSet. In NavigableMap we use methods to return the key value pair like navMap.put(1, "sunday") whereas in NavigableSet we use methods to return values.

Example:

NavigableMap navmap=new ConcurrentSkipListMap();
navmap.put(1, "Sunday");

navmap.put(2, "Monday");

navmap.put(3, "Tuesday");

navmap.put(4, "Wednesday");

navmap.put(5, "Thursday");

navmap.put(6, "Friday");

navmap.put(7, "Saturday");

//Retrieving first data
String first = navmap.firstEntry();
//Retrieving last data
String last = navmap.lastEntry();
//Retrieving the nearest less than or equal to the given key
String closeFloorKeyVal = navmap.floorEntry(5);
//Retrieving the greatest key strictly less than the given key
String less = navmap.lowerEntry(3);
//Retrieving a key-value associated with the least key
//strictly greater than the given key
String higher=navmap.higherEntry(5);
//Removing first
String remFirst = navmap.pollFirstEntry();
//Removing last
String remLast = navmap.pollLastEntry();



Saturday, 14 May 2011

Performance of Map interface implementations

Hashtable

An instance of Hashtable has two parameters that affect its performance: initial capacity and load factor. The capacity is the number of buckets in the hash table, and the initial capacity is simply the capacity at the time the hash table is created. Note that the hash table is open: in the case of a "hash collision", a single bucket stores multiple entries, which must be searched sequentially. The load factor is a measure of how full the hash table is allowed to get before its capacity is automatically increased. The initial capacity and load factor parameters are merely hints to the implementation. The exact details as to when and whether the rehash method is invoked are implementation-dependent.

HashMap

This implementation provides constant-time [ Big O Notation is O(1) ] performance for the basic operations (get and put), assuming the hash function disperses the elements properly among the buckets.
Iteration over collection views requires time proportional to the "capacity" of the HashMap instance (the number of buckets) plus its size (the number of key-value mappings). Thus, it's very important not to set the initial capacity too high (or the load factor too low) if iteration performance is important.

 

TreeMap

The TreeMap implementation provides guaranteed log(n) [ Big O Notation is O(log N) ] time cost for the containsKey, get, put and remove operations.

 

LinkedHashMap

A linked hash map has two parameters that affect its performance: initial capacity and load factor. They are defined precisely as for HashMap. Note, however, that the penalty for choosing an excessively high value for initial capacity is less severe for this class than for HashMap, as iteration times for this class are unaffected by capacity.

Set & List interface extend Collection, but Map interface don't?

Map is considered part of collection framework, but it does not extend collection interface. The reason behind it is the design.But what design do we follow?

And the answer to this questions is best described in Sun's FAQ Page: This was by design. We feel that mappings are not collections and collections are not mappings. Thus, it makes little sense for Map to extend the Collection interface (or vice versa). If a Map is a Collection, what are the elements? The only reasonable answer is "Key-value pairs", but this provides a very limited (and not particularly useful) Map abstraction. You can't ask what value a given key maps to, nor can you delete the entry for a given key without knowing what value it maps to.

Collection could be made to extend Map, but this raises the question: what are the keys? There's no really satisfactory answer, and forcing one leads to an unnatural interface. Maps can be viewed as Collections (of keys, values, or pairs), and this fact is reflected in the three "Collection view operations" on Maps (keySet, entrySet, and values). While it is, in principle, possible to view a List as a Map mapping indices to elements, this has the nasty property that deleting an element from the List changes the Key associated with every element before the deleted element. That's why we don't have a map view operation on Lists.

Sunday, 17 April 2011

Weakhashmap : When value depends on key

Consider the following code:

import java.util.Map;
import java.util.WeakHashMap;

public class TestWeakHeakHashMap
{
private String name = new String("java");
private Map cache = new WeakHashMap<String, DependentObject>();

public void testMethod()
{
cache.put(name, new DependentObject("1", name));

//Discard the strong reference to the key
name = null;
while (true) {
System.gc();
/**
          * Verify Full GC with the -verbose:gc option
            Since there is no strong reference to the key, it is assumed that the
            entry has been removed from the WeakHashMap
          */
System.out.println(cache.size());
}
}

private class DependentObject
{
private String id;
private String name;

public DependentObject(String id, String name)
{
this.id = id;
this.name = name;
}
}
}

Now when the testMethod() is run what do you expect the output to be? Since the strong reference to key is discarded, we assume that the entry from the map would be removed, and map would be empty after a full GC.
But that does not happen though.

Let us see what was the put operation on the WeakHashMap.

cache.put(name, new DependentObject("1", name));


Here the value DependentObject was holding the key name. This would mean that the value always strongly refers to the key, and hence the key would never be garbage collected. The entry would always remain the map.

This is what WeakHashMap API says - "The value objects in a WeakHashMap are held by ordinary strong references. Thus care should be taken to ensure that value objects do not strongly refer to their own keys, either directly or indirectly, since that will prevent the keys from being discarded."