This question already has answers here:
HashMap to return default value for non-found keys?
(16 answers)
Closed 9 years ago.
While using Map as a function argument, only values for 3 keys are populated. However when this function is invoked in another function, the user populates values for initial 2 keys and he does not require 3rd key to be assigned with any value. However if 3rd key is not assigned any value then, the 3rd key is display null value.
Is there any way to avoid this. if user does not assign any value to 3rd key, it must be empty instead of null value.
public String setMapValues(Map<String,String> testMap) throws Exception
{
String str="";
str= testMap.get("a");
str+=testMap.get("b");
str+=testMap.get("c");
info(str);
return str;
}
public void run() throws Exception
{
LinkedHashMap<String,String> myMap = new LinkedHashMap<String,String>();
myMap.put("a", "James");
myMap.put("b", "Bond");
this.setMapValues(myMap);
}
The function calls displays JamesBondnull as the output, instead it should only display JamesBond as the output by ignoring/skipping the null at the end.
You can use a function like
static String nullAsEmpty(Object o) {
return o == null ? "" : o.toString();
}
public String setMapValues(Map<String,String> testMap) {
String str = nullAsEmpty(testMap.get("a")) +
nullAsEmpty(testMap.get("b")) +
nullAsEmpty(testMap.get("c"));
info(str);
return str;
}
How about:
String temp = testMap.get("c");
str+= (temp == null : "" : temp);
You can implement your version of Map:
import java.util.HashMap;
class MyMap<K, V> extends HashMap<K, V> {
#Override
public V get(Object key) {
V val = super.get(key);
if (val != null) {
return val;
} else {
return "";
}
}
}
Then just use MyMap instead of Map
Or init your map with default values, if you know all keys which could be null
Map getInstance(){
Map<String,String> myMap = new LinkedHashMap<String,String>();
myMap.put("a", "");
myMap.put("b", "");
myMap.put("b", "");
return myMap;
}
By putting duplicate keys the old values are replaced by the new ones.
To be complete:
If you use Vadchens answer - which is better - you can do two things:
Extends your map by setting generic value-parameter directly to String
class MySecMap<K> extends LinkedHashMap<K, String>{
#Override
public String get(Object key) {
String val = super.get(key);
if (val != null) {
return val;
} else {
return "";
}
}
}
Or create a class with an extra interface and a default-value-provider:
interface IDefaultValueProvider<V>{
V getDefaultValue();
}
class MyMap<K, V, D extends IDefaultValueProvider<V>> extends LinkedHashMap<K, V>{
private IDefaultValueProvider<V> provider;
public MyMap(IDefaultValueProvider<V> p){
super();
provider = p;
}
#Override
public V get(Object key) {
V val = super.get(key);
if (val != null) {
return val;
} else {
return this.provider.getDefaultValue();
}
}
}
Related
i have the code like this when i create it like this
public final class PhpArray extends AbstractMap
{
private TreeMap t;
private HashMap m;
public PhpArray() {
this.t = new TreeMap(Request.PHP_ARRAY_KEY_COMPARATOR);
this.m = null;
}
#Override
public Object put(final Object key, final Object value) {
if (this.m != null) {
return this.m.put(key, value);
}
try {
return this.t.put(key, value);
}
catch (ClassCastException e) {
this.m = new HashMap(this.t);
this.t = null;
return this.m.put(key, value);
}
}
#Override
public Set entrySet() {
if (this.t != null) {
return this.t.entrySet();
}
return this.m.entrySet();
}
public int arraySize() {
if (this.t == null) {
throw new IllegalArgumentException("The passed PHP \"array\" is not a sequence but a dictionary");
}
if (this.t.size() == 0) {
return 0;
}
return 1 + this.t.lastKey();
}
}
but when i update my project i got error in the code
return 1 + this.t.lastKey();
the error is an arguments + is undefined.. why like that ? and how to fix the problem ?
TreeMap is a generic class but in the code in your question you have used it without type parameters. This means that this line of your code:
private TreeMap t;
is essentially this:
private TreeMap<Object, Object> t;
In other words t.lastKey() returns an Object and the operator + can't be used with Object because an Object is not a number.
Perhaps you meant to call method size() rather than method lastKey()?
Perhaps this tutorial will help?
Is there a way in Java to have a map where the type parameter of a value is tied to the type parameter of a key? What I want to write is something like the following:
public class Foo {
// This declaration won't compile - what should it be?
private static Map<Class<T>, T> defaultValues;
// These two methods are just fine
public static <T> void setDefaultValue(Class<T> clazz, T value) {
defaultValues.put(clazz, value);
}
public static <T> T getDefaultValue(Class<T> clazz) {
return defaultValues.get(clazz);
}
}
That is, I can store any default value against a Class object, provided the value's type matches that of the Class object. I don't see why this shouldn't be allowed since I can ensure when setting/getting values that the types are correct.
EDIT: Thanks to cletus for his answer. I don't actually need the type parameters on the map itself since I can ensure consistency in the methods which get/set values, even if it means using some slightly ugly casts.
You're not trying to implement Joshua Bloch's typesafe hetereogeneous container pattern are you? Basically:
public class Favorites {
private Map<Class<?>, Object> favorites =
new HashMap<Class<?>, Object>();
public <T> void setFavorite(Class<T> klass, T thing) {
favorites.put(klass, thing);
}
public <T> T getFavorite(Class<T> klass) {
return klass.cast(favorites.get(klass));
}
public static void main(String[] args) {
Favorites f = new Favorites();
f.setFavorite(String.class, "Java");
f.setFavorite(Integer.class, 0xcafebabe);
String s = f.getFavorite(String.class);
int i = f.getFavorite(Integer.class);
}
}
From Effective Java (2nd edition) and this presentation.
The question and the answers made me come up with this solution: Type-safe object map. Here is the code. Test case:
import static org.junit.Assert.*;
import java.util.ArrayList;
import java.util.List;
import org.junit.Test;
public class TypedMapTest {
private final static TypedMapKey<String> KEY1 = new TypedMapKey<String>( "key1" );
private final static TypedMapKey<List<String>> KEY2 = new TypedMapKey<List<String>>( "key2" );
#Test
public void testGet() throws Exception {
TypedMap map = new TypedMap();
map.set( KEY1, null );
assertNull( map.get( KEY1 ) );
String expected = "Hallo";
map.set( KEY1, expected );
String value = map.get( KEY1 );
assertEquals( expected, value );
map.set( KEY2, null );
assertNull( map.get( KEY2 ) );
List<String> list = new ArrayList<String> ();
map.set( KEY2, list );
List<String> valueList = map.get( KEY2 );
assertEquals( list, valueList );
}
}
This is the Key class. Note that the type T is never used in this class! It's purely for the purpose of type casting when reading the value out of the map. The field key only gives the key a name.
public class TypedMapKey<T> {
private String key;
public TypedMapKey( String key ) {
this.key = key;
}
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ( ( key == null ) ? 0 : key.hashCode() );
return result;
}
#Override
public boolean equals( Object obj ) {
if( this == obj ) {
return true;
}
if( obj == null ) {
return false;
}
if( getClass() != obj.getClass() ) {
return false;
}
TypedMapKey<?> other = (TypedMapKey<?>) obj;
if( key == null ) {
if( other.key != null ) {
return false;
}
} else if( !key.equals( other.key ) ) {
return false;
}
return true;
}
#Override
public String toString() {
return key;
}
}
TypedMap.java:
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
public class TypedMap implements Map<Object, Object> {
private Map<Object, Object> delegate;
public TypedMap( Map<Object, Object> delegate ) {
this.delegate = delegate;
}
public TypedMap() {
this.delegate = new HashMap<Object, Object>();
}
#SuppressWarnings( "unchecked" )
public <T> T get( TypedMapKey<T> key ) {
return (T) delegate.get( key );
}
#SuppressWarnings( "unchecked" )
public <T> T remove( TypedMapKey<T> key ) {
return (T) delegate.remove( key );
}
public <T> void set( TypedMapKey<T> key, T value ) {
delegate.put( key, value );
}
// --- Only calls to delegates below
public void clear() {
delegate.clear();
}
public boolean containsKey( Object key ) {
return delegate.containsKey( key );
}
public boolean containsValue( Object value ) {
return delegate.containsValue( value );
}
public Set<java.util.Map.Entry<Object, Object>> entrySet() {
return delegate.entrySet();
}
public boolean equals( Object o ) {
return delegate.equals( o );
}
public Object get( Object key ) {
return delegate.get( key );
}
public int hashCode() {
return delegate.hashCode();
}
public boolean isEmpty() {
return delegate.isEmpty();
}
public Set<Object> keySet() {
return delegate.keySet();
}
public Object put( Object key, Object value ) {
return delegate.put( key, value );
}
public void putAll( Map<? extends Object, ? extends Object> m ) {
delegate.putAll( m );
}
public Object remove( Object key ) {
return delegate.remove( key );
}
public int size() {
return delegate.size();
}
public Collection<Object> values() {
return delegate.values();
}
}
No, you can't do it directly. You'll need to write a wrapper class around Map<Class, Object> to enforce that Object will be instanceof Class.
It's possible to create a class which stores a map of type safe key to a value, and cast when necessary. The cast in get method is safe, as after using new Key<CharSequence>(), it's not possible to safely cast it to Key<String> or Key<Object>, so the type system enforces the correct usage of a class.
The Key class needs to be final, as otherwise an user could override equals and cause type-unsafety if two elements with different types were to be equal. Alternatively, it's possible to override equals to be final if you want to use inheritance despite the issues with it.
public final class TypeMap {
private final Map<Key<?>, Object> m = new HashMap<>();
public <T> T get(Key<? extends T> key) {
// Safe, as it's not possible to safely change the Key generic type,
// hash map cannot be accessed by an user, and this class being final
// to prevent serialization attacks.
#SuppressWarnings("unchecked")
T value = (T) m.get(key);
return value;
}
public <T> void put(Key<? super T> key, T value) {
m.put(key, value);
}
public static final class Key<T> {
}
}
You can use below 2 classes, Map class: GenericMap, Map-Key class: GenericKey
For example:
// Create a key includine Type definition
public static final GenericKey<HttpServletRequest> REQUEST = new GenericKey<>(HttpServletRequest.class, "HttpRequestKey");
public void example(HttpServletRequest requestToSave)
{
GenericMap map = new GenericMap();
// Saving value
map.put(REQUEST, requestToSave);
// Getting value
HttpServletRequest request = map.get(REQUEST);
}
Advantages
It forces the user to put and get correct types by compilation error
It's doing casing for you inside
Generic Key helps to avoid write the class type each time you calling put(..) or get
No typo mistakes, like if key is 'String' type
GenericMap
public class GenericMap
{
private Map<String, Object> storageMap;
protected GenericMap()
{
storageMap = new HashMap<String, Object>();
}
public <T> T get(GenericKey<T> key)
{
Object value = storageMap.get(key.getKey());
if (value == null)
{
return null;
}
return key.getClassType().cast(value);
}
/**
* #param key GenericKey object with generic type - T (it can be any type)
* #param object value to put in the map, the type of 'object' mast be - T
*/
public <T> void put(GenericKey<T> key, T object)
{
T castedObject = key.getClassType().cast(object);
storageMap.put(key.getKey(), castedObject);
}
#Override
public String toString()
{
return storageMap.toString();
}
}
GenericKey
public class GenericKey<T>
{
private Class<T> classType;
private String key;
#SuppressWarnings("unused")
private GenericKey()
{
}
public GenericKey(Class<T> iClassType, String iKey)
{
this.classType = iClassType;
this.key = iKey;
}
public Class<T> getClassType()
{
return classType;
}
public String getKey()
{
return key;
}
#Override
public String toString()
{
return "[classType=" + classType + ", key=" + key + "]";
}
}
T as a type must be defined generically in the class instance. The following example works:
public class Test<T> {
private Map<Class<T>, T> defaultValues;
public void setDefaultValue(Class<T> clazz, T value) {
defaultValues.put(clazz, value);
}
public T getDefaultValue(Class<T> clazz) {
return defaultValues.get(clazz);
}
}
Alternatively, you can use Paul Tomblin's answer, and wrap the Map with your own object which will enforce this type of generics.
Is there a way in Java to have a map where the type parameter of a value is tied to the type parameter of a key? What I want to write is something like the following:
public class Foo {
// This declaration won't compile - what should it be?
private static Map<Class<T>, T> defaultValues;
// These two methods are just fine
public static <T> void setDefaultValue(Class<T> clazz, T value) {
defaultValues.put(clazz, value);
}
public static <T> T getDefaultValue(Class<T> clazz) {
return defaultValues.get(clazz);
}
}
That is, I can store any default value against a Class object, provided the value's type matches that of the Class object. I don't see why this shouldn't be allowed since I can ensure when setting/getting values that the types are correct.
EDIT: Thanks to cletus for his answer. I don't actually need the type parameters on the map itself since I can ensure consistency in the methods which get/set values, even if it means using some slightly ugly casts.
You're not trying to implement Joshua Bloch's typesafe hetereogeneous container pattern are you? Basically:
public class Favorites {
private Map<Class<?>, Object> favorites =
new HashMap<Class<?>, Object>();
public <T> void setFavorite(Class<T> klass, T thing) {
favorites.put(klass, thing);
}
public <T> T getFavorite(Class<T> klass) {
return klass.cast(favorites.get(klass));
}
public static void main(String[] args) {
Favorites f = new Favorites();
f.setFavorite(String.class, "Java");
f.setFavorite(Integer.class, 0xcafebabe);
String s = f.getFavorite(String.class);
int i = f.getFavorite(Integer.class);
}
}
From Effective Java (2nd edition) and this presentation.
The question and the answers made me come up with this solution: Type-safe object map. Here is the code. Test case:
import static org.junit.Assert.*;
import java.util.ArrayList;
import java.util.List;
import org.junit.Test;
public class TypedMapTest {
private final static TypedMapKey<String> KEY1 = new TypedMapKey<String>( "key1" );
private final static TypedMapKey<List<String>> KEY2 = new TypedMapKey<List<String>>( "key2" );
#Test
public void testGet() throws Exception {
TypedMap map = new TypedMap();
map.set( KEY1, null );
assertNull( map.get( KEY1 ) );
String expected = "Hallo";
map.set( KEY1, expected );
String value = map.get( KEY1 );
assertEquals( expected, value );
map.set( KEY2, null );
assertNull( map.get( KEY2 ) );
List<String> list = new ArrayList<String> ();
map.set( KEY2, list );
List<String> valueList = map.get( KEY2 );
assertEquals( list, valueList );
}
}
This is the Key class. Note that the type T is never used in this class! It's purely for the purpose of type casting when reading the value out of the map. The field key only gives the key a name.
public class TypedMapKey<T> {
private String key;
public TypedMapKey( String key ) {
this.key = key;
}
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ( ( key == null ) ? 0 : key.hashCode() );
return result;
}
#Override
public boolean equals( Object obj ) {
if( this == obj ) {
return true;
}
if( obj == null ) {
return false;
}
if( getClass() != obj.getClass() ) {
return false;
}
TypedMapKey<?> other = (TypedMapKey<?>) obj;
if( key == null ) {
if( other.key != null ) {
return false;
}
} else if( !key.equals( other.key ) ) {
return false;
}
return true;
}
#Override
public String toString() {
return key;
}
}
TypedMap.java:
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
public class TypedMap implements Map<Object, Object> {
private Map<Object, Object> delegate;
public TypedMap( Map<Object, Object> delegate ) {
this.delegate = delegate;
}
public TypedMap() {
this.delegate = new HashMap<Object, Object>();
}
#SuppressWarnings( "unchecked" )
public <T> T get( TypedMapKey<T> key ) {
return (T) delegate.get( key );
}
#SuppressWarnings( "unchecked" )
public <T> T remove( TypedMapKey<T> key ) {
return (T) delegate.remove( key );
}
public <T> void set( TypedMapKey<T> key, T value ) {
delegate.put( key, value );
}
// --- Only calls to delegates below
public void clear() {
delegate.clear();
}
public boolean containsKey( Object key ) {
return delegate.containsKey( key );
}
public boolean containsValue( Object value ) {
return delegate.containsValue( value );
}
public Set<java.util.Map.Entry<Object, Object>> entrySet() {
return delegate.entrySet();
}
public boolean equals( Object o ) {
return delegate.equals( o );
}
public Object get( Object key ) {
return delegate.get( key );
}
public int hashCode() {
return delegate.hashCode();
}
public boolean isEmpty() {
return delegate.isEmpty();
}
public Set<Object> keySet() {
return delegate.keySet();
}
public Object put( Object key, Object value ) {
return delegate.put( key, value );
}
public void putAll( Map<? extends Object, ? extends Object> m ) {
delegate.putAll( m );
}
public Object remove( Object key ) {
return delegate.remove( key );
}
public int size() {
return delegate.size();
}
public Collection<Object> values() {
return delegate.values();
}
}
No, you can't do it directly. You'll need to write a wrapper class around Map<Class, Object> to enforce that Object will be instanceof Class.
It's possible to create a class which stores a map of type safe key to a value, and cast when necessary. The cast in get method is safe, as after using new Key<CharSequence>(), it's not possible to safely cast it to Key<String> or Key<Object>, so the type system enforces the correct usage of a class.
The Key class needs to be final, as otherwise an user could override equals and cause type-unsafety if two elements with different types were to be equal. Alternatively, it's possible to override equals to be final if you want to use inheritance despite the issues with it.
public final class TypeMap {
private final Map<Key<?>, Object> m = new HashMap<>();
public <T> T get(Key<? extends T> key) {
// Safe, as it's not possible to safely change the Key generic type,
// hash map cannot be accessed by an user, and this class being final
// to prevent serialization attacks.
#SuppressWarnings("unchecked")
T value = (T) m.get(key);
return value;
}
public <T> void put(Key<? super T> key, T value) {
m.put(key, value);
}
public static final class Key<T> {
}
}
You can use below 2 classes, Map class: GenericMap, Map-Key class: GenericKey
For example:
// Create a key includine Type definition
public static final GenericKey<HttpServletRequest> REQUEST = new GenericKey<>(HttpServletRequest.class, "HttpRequestKey");
public void example(HttpServletRequest requestToSave)
{
GenericMap map = new GenericMap();
// Saving value
map.put(REQUEST, requestToSave);
// Getting value
HttpServletRequest request = map.get(REQUEST);
}
Advantages
It forces the user to put and get correct types by compilation error
It's doing casing for you inside
Generic Key helps to avoid write the class type each time you calling put(..) or get
No typo mistakes, like if key is 'String' type
GenericMap
public class GenericMap
{
private Map<String, Object> storageMap;
protected GenericMap()
{
storageMap = new HashMap<String, Object>();
}
public <T> T get(GenericKey<T> key)
{
Object value = storageMap.get(key.getKey());
if (value == null)
{
return null;
}
return key.getClassType().cast(value);
}
/**
* #param key GenericKey object with generic type - T (it can be any type)
* #param object value to put in the map, the type of 'object' mast be - T
*/
public <T> void put(GenericKey<T> key, T object)
{
T castedObject = key.getClassType().cast(object);
storageMap.put(key.getKey(), castedObject);
}
#Override
public String toString()
{
return storageMap.toString();
}
}
GenericKey
public class GenericKey<T>
{
private Class<T> classType;
private String key;
#SuppressWarnings("unused")
private GenericKey()
{
}
public GenericKey(Class<T> iClassType, String iKey)
{
this.classType = iClassType;
this.key = iKey;
}
public Class<T> getClassType()
{
return classType;
}
public String getKey()
{
return key;
}
#Override
public String toString()
{
return "[classType=" + classType + ", key=" + key + "]";
}
}
T as a type must be defined generically in the class instance. The following example works:
public class Test<T> {
private Map<Class<T>, T> defaultValues;
public void setDefaultValue(Class<T> clazz, T value) {
defaultValues.put(clazz, value);
}
public T getDefaultValue(Class<T> clazz) {
return defaultValues.get(clazz);
}
}
Alternatively, you can use Paul Tomblin's answer, and wrap the Map with your own object which will enforce this type of generics.
I have a hash map like,
public static void main(String[] args) {
final Map<String, String> daysMap = new HashMap(7);
daysMap.put("1", "Sunday");
daysMap.put("2", "Monday");
daysMap.put("3", "Tuesday");
daysMap.put("4", "Wednesday");
daysMap.put("5", "Thursday");
daysMap.put("6", "Friday");
daysMap.put("7", "Saturday");
}
In this map
1. Should not allow to put more than 7 elements
2. Should not update value for corresponding key [like daysMap.put("5", "xxx");]
3. Should not allow to remove any key
How to do?
You can implement a new HashMap
public class CoolMap<K, V> extends HashMap<K, V> {
#Override
public V put(K key, V value) {
if (size() == 7) {
throw new IllegalStateException("Size is at max!");
} else {
// If there is something already with that key
if (containsKey(value)) {
// do nothing
return value;
} else {
// put inside
return super.put(key, value);
}
}
}
#Override
public void putAll(Map<? extends K, ? extends V> collection) {
if (collection.size() > 7) {
throw new IllegalStateException("Size is at max!");
} else {
super.putAll(collection);
}
}
#Override
public V remove(Object key) {
return null;// doesn't remove anything
}
Points 2 and 3 are covered by Collections.unmodifiableMap. To cover the first point, you can add an hand written test.
As has been already discussed ,the Points 2 and 3 are covered like this
import java.util.*;
public class OP2 {
public static void main(String[] s) {
//object hash table
Hashtable<String,String> table = new Hashtable<String,String>();
table.
// populate the table
table.put("1", "Sunday");
table.put("2", "Monday");
table.put("3", "Tuesday");
table.put("4", "Wednesday");
table.put("5", "Thursday");
table.put("6", "Friday");
table.put("7", "Saturday");
System.out.println("Initial collection: "+table);
// create unmodifiable map
Map m = Collections.unmodifiableMap(table);
// try to modify the collection
// m.put("key3", "value3");
//Uncomment the above line and an error is obtained
}
}
Moreover for the first problem it would be better to call a function when you populate your map:-
public boolean putAndTest(MyKey key, MyValue value) {
if (map.size() >= MAX && !map.containsKey(key)) {
return false;
} else {
map.put("Whatever you want");
return true;
}
}
Why donĀ“t you create your own object that contains a private hashmap and then you can allow what methods on that private hashmap that are made public?
Imagine you have these property file "list":
this.is.a.key.1 = lk1mldk3ml2k3e2l3k
this.is.a.key.2 = 2309420398402931
this.is.a.key.3 = lksl1k2ml1kmd
and so...
Is there a way to "extract" from the property file all those this.is.a.key.* keys without having to hard-code it one by one?
The reason I'm asking this is because I don't want this:
this.is.a.list.key = 12983798123,82193819mx91,23891293819283,3892938492834
since I need those "1,2,3..." names inside the key name to distinct one key from another.
Any solution? Or I have to hardcode it?
I wrote MapFilter class to do this for me:
/*
* Note that all access through the filter reference the underlying Map so
* adding to a MapFilder results in additions to the Map.
*/
public class MapFilter<T> implements Map<String, T> {
// The enclosed map -- could also be a MapFilter.
final private Map<String, T> map;
// Use a TreeMap for predictable iteration order.
// Store Map.Entry to reflect changes down into the underlying map.
// The Key is the shortened string. The entry.key is the full string.
final private Map<String, Map.Entry<String, T>> entries = new TreeMap<String, Map.Entry<String, T>>();
// The prefix they are looking for in this map.
final private String prefix;
public MapFilter(Map<String, T> map, String prefix) {
// Store my backing map.
this.map = map;
// Record my prefix.
this.prefix = prefix;
// Build my entries.
rebuildEntries();
}
public MapFilter(Map<String, T> map) {
this(map, "");
}
private synchronized void rebuildEntries() {
// Start empty.
entries.clear();
// Build my entry set.
for (Map.Entry<String, T> e : map.entrySet()) {
String key = e.getKey();
// Retain each one that starts with the specified prefix.
if (key.startsWith(prefix)) {
// Key it on the remainder.
String k = key.substring(prefix.length());
// Entries k always contains the LAST occurrence if there are multiples.
entries.put(k, e);
}
}
}
#Override
public String toString() {
StringBuilder s = new StringBuilder("MapFilter (" + prefix + ") of " + map + " containing ").append(entrySet());
return s.toString();
}
// Constructor from a properties file.
public MapFilter(Properties p, String prefix) {
// Properties extends HashTable<Object,Object> so it implements Map.
// I need Map<String,T> so I wrap it in a HashMap for simplicity.
this(new HashMap<String, T>((Map) p), prefix);
}
// Helper to fast filter the map.
public MapFilter<T> filter(String prefix) {
// Wrap me in a new filter.
return new MapFilter<T>(this, prefix);
}
// Count my entries.
public int size() {
return entries.size();
}
// Are we empty.
public boolean isEmpty() {
return entries.isEmpty();
}
// Is this key in me?
public boolean containsKey(Object key) {
return entries.containsKey(key);
}
// Is this value in me.
public boolean containsValue(Object value) {
// Walk the values.
for (Map.Entry<String, T> e : entries.values()) {
if (value.equals(e.getValue())) {
// Its there!
return true;
}
}
return false;
}
// Get the referenced value - if present.
public T get(Object key) {
return get(key, null);
}
// Get the referenced value - if present.
public T get(Object key, T dflt) {
Map.Entry<String, T> e = entries.get((String)key);
return e != null ? e.getValue() : dflt;
}
// Add to the underlying map.
public T put(String key, T value) {
T old = null;
// Do I have an entry for it already?
Map.Entry<String, T> entry = entries.get(key);
// Was it already there?
if (entry != null) {
// Yes. Just update it.
old = entry.setValue(value);
} else {
// Add it to the map.
map.put(prefix + key, value);
// Rebuild.
rebuildEntries();
}
return old;
}
// Get rid of that one.
public T remove(Object key) {
// Do I have an entry for it?
Map.Entry<String, T> entry = entries.get((String)key);
if (entry != null) {
entries.remove(key);
// Change the underlying map.
return map.remove(prefix + key);
}
return null;
}
// Add all of them.
public void putAll(Map<? extends String, ? extends T> m) {
for (Map.Entry<? extends String, ? extends T> e : m.entrySet()) {
put(e.getKey(), e.getValue());
}
}
// Clear everything out.
public void clear() {
// Just remove mine. This does not clear the underlying map.
for (String key : entries.keySet()) {
map.remove(prefix + key);
}
entries.clear();
}
public Set<String> keySet() {
return entries.keySet();
}
public Collection<T> values() {
// Roll them all out into a new ArrayList.
List<T> values = new ArrayList<T>();
for (Map.Entry<String, T> v : entries.values()) {
values.add(v.getValue());
}
return values;
}
public Set<Map.Entry<String, T>> entrySet() {
// Roll them all out into a new TreeSet.
Set<Map.Entry<String, T>> entrySet = new TreeSet<Map.Entry<String, T>>();
for (Map.Entry<String, Map.Entry<String, T>> v : entries.entrySet()) {
entrySet.add(new Entry<T>(v));
}
return entrySet;
}
/**
* An entry.
*
* #param <T>
*
* The type of the value.
*/
private static class Entry<T> implements Map.Entry<String, T>, Comparable<Entry<T>> {
// Note that entry in the entry is an entry in the underlying map.
private final Map.Entry<String, Map.Entry<String, T>> entry;
Entry(Map.Entry<String, Map.Entry<String, T>> entry) {
this.entry = entry;
}
public String getKey() {
return entry.getKey();
}
public T getValue() {
// Remember that the value is the entry in the underlying map.
return entry.getValue().getValue();
}
public T setValue(T newValue) {
// Remember that the value is the entry in the underlying map.
return entry.getValue().setValue(newValue);
}
#Override
public boolean equals(Object o) {
if (!(o instanceof Entry)) {
return false;
}
Entry e = (Entry) o;
return getKey().equals(e.getKey()) && getValue().equals(e.getValue());
}
#Override
public int hashCode() {
return getKey().hashCode() ^ getValue().hashCode();
}
#Override
public String toString() {
return getKey() + "=" + getValue();
}
public int compareTo(Entry<T> o) {
return getKey().compareTo(o.getKey());
}
}
// Simple tests.
public static void main(String[] args) {
String[] samples = {
"Some.String.For.Me",
"Some.String.For.You",
"Some.More",
"Yet.More"};
Map map = new HashMap();
for ( String s : samples ) {
map.put(s, s);
}
Map all = new MapFilter(map);
Map some = new MapFilter(map, "Some.");
Map someString = new MapFilter(some, "String.");
System.out.println("All: "+all);
System.out.println("Some: "+some);
System.out.println("Some.String: "+someString);
}
}