The following class demonstrates which
Map
implementations and which nested Map
classes are Serializable
and which are not for several popular standard Map
implementations.package dustin.examples;
import java.io.Serializable;
import java.util.Collection;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.WeakHashMap;
import java.util.concurrent.ConcurrentHashMap;
import static java.lang.System.out;
/**
* This class demonstrates that the nested classes for various types of Java
* maps are not Serializable.
*/
public class NonSerializableCollectionsInnerClassesDemonstrator
{
final String NEW_LINE = System.getProperty("line.separator");
/** Enum describing types of Maps used in this demonstration. */
private enum MapTypeEnum
{
CONCURRENT_HASH
{
public Map<Long, String> newSingleEntryMapInstance()
{
final Map<Long, String> map = new ConcurrentHashMap<Long, String>();
map.put(1L, "One");
return map;
}
},
HASH
{
public Map<Long, String> newSingleEntryMapInstance()
{
final Map<Long, String> map = new HashMap<Long, String>();
map.put(2L, "Two");
return map;
}
},
HASH_TABLE
{
public Map<Long, String> newSingleEntryMapInstance()
{
final Map<Long, String> map = new Hashtable<Long, String>();
map.put(3L, "Three");
return map;
}
},
LINKED_HASH
{
public Map<Long, String> newSingleEntryMapInstance()
{
final Map<Long, String> map = new LinkedHashMap<Long, String>();
map.put(4L, "Four");
return map;
}
},
TREE
{
public Map<Long, String> newSingleEntryMapInstance()
{
final Map<Long, String> map = new TreeMap<Long, String>();
map.put(5L, "Five");
return map;
}
},
WEAK_HASH
{
public Map<Long, String> newSingleEntryMapInstance()
{
final Map<Long, String> map = new WeakHashMap<Long, String>();
map.put(6L, "Six");
return map;
}
};
public abstract Map<Long, String> newSingleEntryMapInstance();
}
/**
* Indicate where the provided class defines a class that implements the
* java.io.Serializable interface.
*
* @param candidateClass Class whose serializable status is desired.
* @return {@code true} if the provided class is Serializable.
*/
public static boolean isSerializable(final Object candidateClass)
{
return candidateClass instanceof Serializable;
}
/**
* Print (to stdout) a simple message describing if the provided object
* is serializable or not.
*
* @param object Object whose class's status as Serializable or not is to be
* printed to stdout.
*/
public static void printSerializableStatus(final Object object)
{
final Class clazz = object.getClass();
out.println(
clazz.getName() + " is " + (isSerializable(object) ? "" : "NOT ") + "Serializable.");
}
/**
* Process a provided Map instance to indicate if the Map itself and its
* nested classes are Serializable.
*
* @param header Header to be printed before Serializable results are printed.
* @param map Map to be evaluated for Serializability or it and its nested classes.
*/
private void processSpecificMapInstance(
final String header, final Map<Long, String> map)
{
out.println(NEW_LINE + "===== " + header + " =====");
printSerializableStatus(map);
final Set<Long> mapKeySet = map.keySet();
printSerializableStatus(mapKeySet);
final Iterator<Long> mapKeySetIterator = mapKeySet.iterator();
printSerializableStatus(mapKeySetIterator);
final Collection<String> mapValues = map.values();
printSerializableStatus(mapValues);
for (final Map.Entry<Long, String> mapEntrySet : map.entrySet())
{
printSerializableStatus(mapEntrySet);
}
}
/**
* Method for explicitly creating EnumMap to check it and its nested classes
* for Serializability.
*/
private void processAndDemonstrateEnumMap()
{
out.println(NEW_LINE + "===== ENUMMAP =====");
final Map<MapTypeEnum, String> map = new EnumMap(MapTypeEnum.class);
map.put(MapTypeEnum.HASH, "HashMap");
printSerializableStatus(map);
final Set<MapTypeEnum> mapKeySet = map.keySet();
printSerializableStatus(mapKeySet);
final Iterator<MapTypeEnum> mapKeySetIterator = mapKeySet.iterator();
printSerializableStatus(mapKeySetIterator);
final Collection<String> mapValues = map.values();
printSerializableStatus(mapValues);
for (final Map.Entry<MapTypeEnum, String> mapEntrySet : map.entrySet())
{
printSerializableStatus(mapEntrySet);
}
}
/**
* Demonstrate Serializability status of different Map implementations and
* the Serializability of those Map instance's nested classes.
*/
private void demonstrateMapNestedClassesSerializability()
{
final Map<Long, String> hashMap = MapTypeEnum.HASH.newSingleEntryMapInstance();
processSpecificMapInstance("HASHMAP", hashMap);
final Map<Long, String> linkedHashMap = MapTypeEnum.LINKED_HASH.newSingleEntryMapInstance();
processSpecificMapInstance("LINKEDHASHMAP", linkedHashMap);
final Map<Long, String> concurrentHashMap = MapTypeEnum.CONCURRENT_HASH.newSingleEntryMapInstance();
processSpecificMapInstance("CONCURRENTHASHMAP", concurrentHashMap);
final Map<Long, String> weakHashMap = MapTypeEnum.WEAK_HASH.newSingleEntryMapInstance();
processSpecificMapInstance("WEAKHASHMAP", weakHashMap);
final Map<Long, String> treeMap = MapTypeEnum.TREE.newSingleEntryMapInstance();
processSpecificMapInstance("TREEMAP", treeMap);
final Map<Long, String> hashTable = MapTypeEnum.HASH_TABLE.newSingleEntryMapInstance();
processSpecificMapInstance("HASHTABLE", hashTable);
processAndDemonstrateEnumMap();
}
/**
* Main executable function to run the demonstrations.
*
* @param arguments Command-line arguments; none expected.
*/
public static void main(final String[] arguments)
{
final NonSerializableCollectionsInnerClassesDemonstrator instance =
new NonSerializableCollectionsInnerClassesDemonstrator();
instance.demonstrateMapNestedClassesSerializability();
}
}
The above class runs through several popular
Map
implementations (EnumMap, HashMap, LinkedHashMap, TreeMap, Hashtable, WeakHashMap, ConcurrentHashMap) and prints out whether each Map
implementation and its nested classes are Serializable
. The output it generates is shown next.===== HASHMAP =====
java.util.HashMap is Serializable.
java.util.HashMap$KeySet is NOT Serializable.
java.util.HashMap$KeyIterator is NOT Serializable.
java.util.HashMap$Values is NOT Serializable.
java.util.HashMap$Entry is NOT Serializable.
===== LINKEDHASHMAP =====
java.util.LinkedHashMap is Serializable.
java.util.HashMap$KeySet is NOT Serializable.
java.util.LinkedHashMap$KeyIterator is NOT Serializable.
java.util.HashMap$Values is NOT Serializable.
java.util.LinkedHashMap$Entry is NOT Serializable.
===== CONCURRENTHASHMAP =====
java.util.concurrent.ConcurrentHashMap is Serializable.
java.util.concurrent.ConcurrentHashMap$KeySet is NOT Serializable.
java.util.concurrent.ConcurrentHashMap$KeyIterator is NOT Serializable.
java.util.concurrent.ConcurrentHashMap$Values is NOT Serializable.
java.util.concurrent.ConcurrentHashMap$WriteThroughEntry is Serializable.
===== WEAKHASHMAP =====
java.util.WeakHashMap is NOT Serializable.
java.util.WeakHashMap$KeySet is NOT Serializable.
java.util.WeakHashMap$KeyIterator is NOT Serializable.
java.util.WeakHashMap$Values is NOT Serializable.
java.util.WeakHashMap$Entry is NOT Serializable.
===== TREEMAP =====
java.util.TreeMap is Serializable.
java.util.TreeMap$KeySet is NOT Serializable.
java.util.TreeMap$KeyIterator is NOT Serializable.
java.util.TreeMap$Values is NOT Serializable.
java.util.TreeMap$Entry is NOT Serializable.
===== HASHTABLE =====
java.util.Hashtable is Serializable.
java.util.Collections$SynchronizedSet is Serializable.
java.util.Hashtable$Enumerator is NOT Serializable.
java.util.Collections$SynchronizedCollection is Serializable.
java.util.Hashtable$Entry is NOT Serializable.
===== ENUMMAP =====
java.util.EnumMap is Serializable.
java.util.EnumMap$KeySet is NOT Serializable.
java.util.EnumMap$KeyIterator is NOT Serializable.
java.util.EnumMap$Values is NOT Serializable.
java.util.EnumMap$EntryIterator is NOT Serializable.
The output shown above leads to several interesting observations. First, and most importantly from this post's perspective, is the fact that most (all but
WeakHashMap
) of the Map
implementations are themselves Serializable
, but most of them (all but Hashtable
) have nested classes (for keyset, values, and entryset) that are NOT Serializable
.There are several approaches that can be used if the data from one of these classes nested within Map need to be distributed. The previously referenced bug reports provide an obvious "work around." One can copy the returned key set into its own new (and Serializable) Set:
Copy the values of the Set returned byThis blog post has attempted to demonstrate thatkeySet()
,entrySet()
orvalues()
into a newHashSet
orTreeSet
object:Set obj = new HashSet(myMap.keySet());
Serializable
cannot be taken for granted. This is particularly true when dealing with nested classes in Map
implementations.
Không có nhận xét nào:
Đăng nhận xét