Java key value pair without using collections - java

How would I manage key value pair as in HashMap without using Java Collections.
I found this (Pair class from Commons Lang)
Pair<String, String> keyValue = new ImmutablePair("key", "value");
Is this the only way to handle? Or are there other ways to handle?

personally i wouldn't, i like the built-in collections just fine.
having said that...
if you want something with better performance/smaller in-memory size, have a look at trove. they have map implementations that do away with the extra Entry (key-value pair) class. theyre especially good if your keys or values are primitives (say, ints)
if you want something with better predictability (another performance aspect), have a look at javolution. their collections classes offer much better worst-case times (think what happens when you insert something into a standard collection and you just triggered a resize...)
if youre looking for collections classes with more features, have a look at guava

Try Generics and create your own class if you don't like java collections:
public class Pair<K, V> {
private K key;
private V value;
public Pair(K key, V value) {
this.key = key;
this.value = value;
}
public K getKey() {
return key;
}
public void setKey(K key) {
this.key = key;
}
public V getValue() {
return value;
}
public void setValue(V value) {
this.value = value;
}
public static void main(String[] args) {
Pair<String, String> pair = new Pair<String, String>("key", "value");
System.out.println(pair.getKey());
System.out.println(pair.getValue());
}
}

A Map is not a Collection, so use a HashMap.

Related

Is this wrapper of LinkedHashMap thread safe? If not how can it become thread safe?

I am attempting to implement a class with the following features by wrapping one of the built in Map classes.
Basic map functionality. (Only basic put, get, remove)
Can iterate over the values of the map in the order they were added. (as in LinkedHashMap)
Is thread safe.
Currently using a generic implementation but in the current use-case there will only ever be a handful of objects in the map. And additions/removal happen extremely infrequently - nominally additions occur only once.
Basically this one container should provide clients the ability to lookup a single Value object by Key AND/OR iterate through the Values (with order guarantee). In either case, the caller will likely be modifying the Value object, so it can't be read-only. Finally, callers may be coming from multiple threads.
This a minimized version of what I have right now:
public class MapWrapper<K, V> implements Iterable<V>
{
private Map<K, V> map = new LinkedHashMap<K, V>();
public void add(K key, V value)
{
// Does some other stuff
synchronized (map)
{
map.put(key, value);
}
}
public V get(K key)
{
V retVal;
synchronized (map)
{
retVal = map.get(key);
}
return retVal;
}
#Override
public Iterator<V> iterator()
{
List<V> values = new ArrayList<V>(map.values());
return values.iterator();
}
}
I feel like the iterator part is preventing this from being fully thread-safe. I see classes such as ConcurrentHashMap state that any client obtaining an iterator on the object MUST manually synchronize on the map object itself. Is there a way to make the code above thread-safe but still allow clients direct iterator access? Ie, I would like to be able to use a for-in loop, but I can not synchronize on the underlying map within MapWrapper.
MapWrapper<String, Object> test = new MapWrapper<String,Object>();
test.add("a", new Object());
test.add("c", new Object());
for (Object o: test) { o.setSomething(); }
I believe the following solves the issue by keeping ordered and hashed references, while maintaining thread safety with minimal effort:
import java.util.Iterator;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
public class OrderedConcurrentHashMap<K, V> implements Iterable<V>
{
private ConcurrentHashMap<K, V> map = new ConcurrentHashMap<>();
private ConcurrentLinkedQueue<V> queue = new ConcurrentLinkedQueue<>();
public void add(K key, V value)
{
map.put(key, value);
queue.add(value);
}
public V get(K key)
{
return map.get(key);
}
public boolean remove(K key)
{
return queue.remove(map.remove(key));
}
#Override
public Iterator<V> iterator()
{
return queue.iterator();
}
}
Given the following from the OP:
Only a handful of items
Rarely will items be added or removed
This is probably an optimal solution using only built-in collections and concurrency utilities.
The remove method here can be modified according to behavior that clients expect; this simplest implementation is just a suggestion.
Of particular note from the ConcurrentLinkedQueue docs for Java 8:
Iterators are weakly consistent, returning elements reflecting the state of the queue at some point at or since the creation of the iterator. They do not throw ConcurrentModificationException, and may proceed concurrently with other operations. Elements contained in the queue since the creation of the iterator will be returned exactly once.
And:
This class and its iterator implement all of the optional methods of the Queue and Iterator interfaces.
Assuming that you ensure V is thread-safe, this wrapper collection should ensure container thread safety.
Another thing to keep in mind is that the java.util.concurrent collections are not null-tolerant (ConcurrentHashMap.put(k, v), ConcurrentLinkedQueue.add(v), ConcurrentHashMap.get(k)).
From the put(k, v) doc:
Throws:
NullPointerException - if the specified key or value is null
From the add(v) doc:
Throws:
NullPointerException - if the specified element is null
From the get(k) doc:
Throws:
NullPointerException - if the specified key is null
I'm still thinking about how this would be handled.
It seems that introducing nulls significantly complicates things (as it always does).
EDIT: After some research, I found this: https://stackoverflow.com/a/9298113
I did come up with an extension of the implementation I shared above that handles nulls, but I'd be uncomfortable regarding race conditions outside of an experimental setting.
Planning on taking advantage of
java.util.concurrent.ConcurrentSkipListMap
The "natural ordering" provided by the keys being used is sufficient.
I think this should work.
public class MapWrapper<K, V> implements Iterable<V> {
private Map<K, V> map = new LinkedHashMap<K, V>();
private int currentSize = 0;
public void add(K key, V value) {
// Does some other stuff
synchronized (map) {
map.put(key, value);
currentSize++;
}
}
public V get(K key) {
V retVal;
synchronized (map) {
retVal = map.get(key);
currentSize--;
}
return retVal;
}
#Override
public Iterator<V> iterator() {
return new SyncIterator();
}
// Inner class example
private class SyncIterator implements Iterator<V> {
private int currentIndex = 0;
#Override
public boolean hasNext() {
return currentIndex < currentSize;
}
#Override
public V next() {
synchronized (map) {
List<V> values = new ArrayList<V>(map.values());
return values.get(currentIndex++);
}
}
#Override
public void remove() {
throw new UnsupportedOperationException();
}
}
}

Cross between an Enum and a Dictionary

In my Java project, I have a need to work with a handful of strings (about 10-30 at a time). I want a data structure to hold them, with properties like so:
Can assign a unique name to each string
The unique names can be used in the code just as if they were variables, with support for IDE auto-complete, no calling getValue() or toString(), etc.
Can iterate over each value in the data structure
In practice, I'd want the code to look something like this:
MagicalDataStructure<String> mds = new MagicalDataStructure(
FirstString = "foo",
SecondString = "bar",
);
/*
This section would output:
foo
bar
*/
for (String value : mds) {
System.out.println(value);
}
/*
This section would output:
The first value is: foo
*/
System.out.println("The first value is: " + FirstString);
Things I've considered:
A class full of static finals. This satisfies #1 and #2, but I can't iterate over them -- at least not without resorting to dark-mojo reflection.
A dictionary. This satisfies #1 and #3, but the keys wouldn't be auto-completable, and there's additional syntax involved in accessing the values.
An enum. This also solves #1 and #3, but accessing the string value takes a little bit of extra code.
Is there a data structure, library, etc that will do what I want?
I would definitely favor a Map for this:
public enum PagePath {
PATH1,
PATH2,
// etc.
}
public static final Map<PagePath, String> ALL_PATHS;
static {
Map<PagePath, String> paths = new EnumMap<>(PagePath.class);
paths.put(PagePath.PATH1, "/html/div[0]/h1");
paths.put(PagePath.PATH2, "/html//form/input[id='firstname']");
// etc.
// Make sure no one breaks things by removing entries
// or by adding enum constants while forgetting to account
// for them in the above Map.
if (!paths.keySet().equals(EnumSet.allOf(PagePath.class))) {
throw new RuntimeException(
"Map does not have entries for all PagePath constants!");
}
ALL_PATHS = Collections.unmodifiableMap(paths);
}
Another possibility, as you’ve mentioned, is using String constants. You can place the initialization of those constants inside the initialization of the “all values” list, to make sure none of them are forgotten:
public static final String PATH1;
public static final String PATH2;
// etc.
public static final Collection<String> ALL_PATHS;
static {
ALL_PATHS = Collections.unmodifiableCollection(Arrays.asList(
PATH1 = "/html/div[0]/h1",
PATH2 = "/html//form/input[id='firstname']",
// etc.
));
}
If someone removes a constant, they’ll be forced to remove its initialization from the Arrays.asList call. If someone adds a constant, and keeps it consistent with the other constants’ declarations, they will be forced to add it to the ALL_PATHS List, since failing to do so would mean it never gets initialized, which compiler will catch.
If your strings are properties your may want to use RessourceBundle or Properties. This can be use to solve problem 1/3.
To solve problem 2, you may create Enum that are Keys to your HashMap so that you need to write hashMap.get(enum) that will auto-complete everything. This solution add words but benefit from auto-completion.
Can you just write a custom method to return the string values using enum?
public enum MagicalDataStructure {
FirstString("foo"),
SecondString("bar");
String value;
MagicalDataStructure(String value) {
this.value = value;
}
public static List<String> getMagicalStrings() {
List<String> strings = new ArrayList<String>();
for (MagicalDataStructure item : MagicalDataStructure.values()) {
strings.add(item.value);
}
return strings;
}
}
And call the function wherever you need to iterate:
public static void main(String[] args) {
for (String magicalString: MagicalDataStructure.getMagicalStrings()) {
System.out.println(magicalString);
}
}
How about this :) The main idea here is the following we use the EnumMap as a base for our CustomEnumMap. My understanding is that you don't need put methods so our first task is to actually throw Unsupported Operation for them. The second step is to define the different enums with the values they are actually representing. The third step is achieved through a static method that converts any Enumeration into our CustomEnumMap. How the map is later used you can see for yourself.
There is one place for improvement though and it is the implementation of the static method. Unfortunately I am just learning java 8 lambdas so I was not able to implement it fast in a good way. But I will work on that and will give you the final implementation of this method later. Or is someone wants to help me out with it is welcome.
public static class CustomEnumMap<K extends Enum<K>,V> extends EnumMap<K, V> {
public CustomEnumMap(EnumMap<K, ? extends V> m) {
super(m);
}
#Override
public V put(K key, V value) {
throw new UnsupportedOperationException();
}
#Override
public void putAll(Map<? extends K, ? extends V> m) {
throw new UnsupportedOperationException();
}
}
public static enum EnumA {
FIRST("value1"),SECOND("value2"),THREE("value3");
private String value;
private EnumA(String value) {
this.value = value;
}
public String toString() {
return value;
}
}
public static enum EnumB {
FIRST("value1"),SECOND("value2");
private String value;
private EnumB(String value) {
this.value = value;
}
public String toString() {
return value;
}
}
public static <T extends Enum<T>> CustomEnumMap<T, String> toMap(T[] myenum) {
return new CustomEnumMap<T,String>(new EnumMap<T,String>( Arrays.stream(myenum).collect(Collectors.toMap(t->(T)t, t->t.toString()))));
}
public static void main(String args[]) {
CustomEnumMap<EnumA, String> enumA = toMap(EnumA.values());
CustomEnumMap<EnumA, String> enumB = toMap(EnumA.values());
for (String stringA : enumA.values()) {
System.out.print(stringA);
}
System.out.println("");
for (String stringB : enumB.values()) {
System.out.print(stringB);
}
}

Which data structure in Java should I use for storing key value pairs in ordered form? No duplicates [duplicate]

This question already has answers here:
java collection that has key/value pair, and is ordered according to insert order
(4 answers)
Closed 10 years ago.
I am working on a project and I need to store key value pairs (one-to-one mapping) in an ordered fashion. Then I should be able to retrieve the key using the value and value using the key. I have looked at Maps, Sets and Hash Tables, but they aren't ordered.
Also, though trivial, it would be great if we could DS retrieve the keys and values at once i.e., the interface supports such functions.
EDIT: The keys and values are all unique. Maintaining the inserted order is good enough.
Notice that you don't define what counts as "ordered". A LinkedHashMap enables iterating over the keys (and therefore values) in insertion-order. Conversely, a TreeMap lets you specify a sort order with a comparator, and ensures all items added to the map are stored in sorted order. 99 times out of 100, one of these classes should be all you need. Alternatively, Google's Guava project has several very nice BiMap implementations that you may find fits your needs.
I strongly caution you: if you think you need more than what these classes can provide, you are likely over-engineering your problem.
For reasons I can't fully justify, I implemented a proper UniqueOrderedBiMap for you, which is compatible with the Java Collections framework and all implemented functions run efficiently. You can use whatever underlying map you see fit (including an un-ordered map, if you really wanted) and keys and values are always unique. Notice that it is a very thin wrapper around a LinkedHashMap, because that's all you need, a LinkedHashMap with extra checks to ensure Values remain unique.
For the curious, check this answers revision history for a UniqueOrderedMap which lacks the getKey() and removeKey() methods, but more properly implements the Map interface, and only needs a HashSet, rather than a HashMap, to store the known values.
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
public class UniqueOrderedBiMap<K, V>implements Map<K, V> {
private Map<K, V> orderedMap;
private HashMap<V, K> valueMap;
public UniqueOrderedBiMap() {
this(new LinkedHashMap<K,V>());
}
public UniqueOrderedBiMap(Map<K, V> underlyingMap) {
orderedMap = underlyingMap;
valueMap = new HashMap<V, K>(orderedMap.size());
for(Map.Entry<K, V> e : orderedMap.entrySet()) {
if(!valueMap.containsKey(e.getValue())) { // Duplicate value
// could instead fail softly by removing the associated item from the map, but this seems cleaner/clearer.
// generally this constructor should be passed an empty map anyways
throw new IllegalArgumentException("Duplicate value "+e.getValue()+" found in underlying map.");
}
valueMap.put(e.getValue(), e.getKey());
}
}
#Override
public int size() {
return orderedMap.size();
}
#Override
public boolean isEmpty() {
return orderedMap.isEmpty();
}
#Override
public boolean containsKey(Object key) {
return orderedMap.containsKey(key);
}
#Override
public boolean containsValue(Object value) {
// more efficient than iterating over the map
return valueMap.containsKey(value);
}
#Override
public V get(Object key) {
return orderedMap.get(key);
}
public K getKey(V value) {
return valueMap.get(value);
}
// Likely want to implement a forcePut(K, V) method like Guava's BiMaps do
#Override
public V put(K key, V value) {
if(valueMap.containsKey(value)) {
throw new IllegalArgumentException("Cannot insert non-unique value "+value);
}
V ret = orderedMap.put(key, value);
valueMap.remove(ret);
valueMap.put(value, key);
return ret;
}
#Override
public V remove(Object key) {
V ret = orderedMap.remove(key);
valueMap.remove(ret);
return ret;
}
public K removeKey(V value) {
K ret = valueMap.remove(value);
orderedMap.remove(ret);
return ret;
}
#Override
public void putAll(Map<? extends K, ? extends V> m) {
// Existing Map implementation's putAll have some optimizations we
// could take advantage of, but this isn't unreasonable for a first pass
for(Entry<? extends K, ? extends V> e : m.entrySet()) {
put(e.getKey(), e.getValue());
}
}
#Override
public void clear() {
orderedMap.clear();
valueMap.clear();
}
#Override
public Set<K> keySet() {
return orderedMap.keySet();
}
#Override
public Collection<V> values() {
return orderedMap.values();
}
#Override
public Set<java.util.Map.Entry<K, V>> entrySet() {
return orderedMap.entrySet();
}
#Override
public boolean equals(Object o) {
if(o instanceof UniqueOrderedBiMap) {
UniqueOrderedBiMap<?,?> map = (UniqueOrderedBiMap<?,?>)o;
return orderedMap.equals(map.orderedMap);
}
return false;
}
#Override
public int hashCode() {
return orderedMap.hashCode();
}
#Override public String toString() {
return orderedMap.toString();
}
public static void main(String[] args) {
String[] names = { "Marcus", "Jim", "Tom", "Sam" };
String[] grades = { "A", "B", "D", "F" };
UniqueOrderedBiMap<String,String> insertionMap = new UniqueOrderedBiMap<>();
UniqueOrderedBiMap<String,String> sortedMap = new UniqueOrderedBiMap<>(new TreeMap<String,String>());
for(int i = 0; i < names.length; i++) {
insertionMap.put(names[i], grades[i]);
sortedMap.put(names[i], grades[i]);
}
// Poor man's assert
System.out.println(insertionMap.toString().equals("{Marcus=A, Jim=B, Tom=D, Sam=F}"));
System.out.println(sortedMap.toString().equals("{Jim=B, Marcus=A, Sam=F, Tom=D}"));
insertionMap.put("Tom", "C");
sortedMap.put("Tom", "C");
System.out.println(insertionMap.toString().equals("{Marcus=A, Jim=B, Tom=C, Sam=F}"));
System.out.println(sortedMap.toString().equals("{Jim=B, Marcus=A, Sam=F, Tom=C}"));
try {
insertionMap.put("Sam", "C");
} catch (IllegalArgumentException e) {
System.out.println(e.getMessage());
}
try {
sortedMap.put("Sam", "C");
} catch (IllegalArgumentException e) {
System.out.println(e.getMessage());
}
insertionMap.remove("Tom");
sortedMap.remove("Tom");
insertionMap.put("Sam", "C");
sortedMap.put("Sam", "C");
System.out.println(insertionMap.toString().equals("{Marcus=A, Jim=B, Sam=C}"));
System.out.println(sortedMap.toString().equals("{Jim=B, Marcus=A, Sam=C}"));
insertionMap.removeKey("A");
sortedMap.removeKey("A");
System.out.println(insertionMap.toString().equals("{Jim=B, Sam=C}"));
System.out.println(sortedMap.toString().equals("{Jim=B, Sam=C}"));
}
}
If you can use third party libraries then consider using an ImmutableBiMap. Its a Guava Collection class that provides
User specified iteration order
Normal mapping from keys to values and inverse mapping from values to keys
The one consideration is that once created the map is immutable and cannot be modified.
LinkedHashMap should be suitable for you. Read through the link
You will need two LinkedHashMap. You can create custom class that internally uses two LinkedHashMap. One for mapping keys to value and another one for mapping values to key.

Does Java have a HashMap with reverse lookup?

I have data that is organized in kind of a "key-key" format, rather than "key-value". It's like a HashMap, but I will need O(1) lookup in both directions. Is there a name for this type of data structure, and is anything like this included in Java's standard libraries? (or maybe Apache Commons?)
I could write my own class that basically uses two mirrored Maps, but I'd rather not reinvent the wheel (if this already exists but I'm just not searching for the right term).
There is no such class in the Java API. The Apache Commons class you want is going to be one of the implementations of BidiMap.
As a mathematician, I would call this kind of structure a bijection.
In addition to Apache Commons, Guava also has a BiMap.
Here is a simple class I used to get this done (I did not want to have yet another third party dependency). It does not offer all features available in Maps but it is a good start.
public class BidirectionalMap<KeyType, ValueType>{
private Map<KeyType, ValueType> keyToValueMap = new ConcurrentHashMap<KeyType, ValueType>();
private Map<ValueType, KeyType> valueToKeyMap = new ConcurrentHashMap<ValueType, KeyType>();
synchronized public void put(KeyType key, ValueType value){
keyToValueMap.put(key, value);
valueToKeyMap.put(value, key);
}
synchronized public ValueType removeByKey(KeyType key){
ValueType removedValue = keyToValueMap.remove(key);
valueToKeyMap.remove(removedValue);
return removedValue;
}
synchronized public KeyType removeByValue(ValueType value){
KeyType removedKey = valueToKeyMap.remove(value);
keyToValueMap.remove(removedKey);
return removedKey;
}
public boolean containsKey(KeyType key){
return keyToValueMap.containsKey(key);
}
public boolean containsValue(ValueType value){
return keyToValueMap.containsValue(value);
}
public KeyType getKey(ValueType value){
return valueToKeyMap.get(value);
}
public ValueType get(KeyType key){
return keyToValueMap.get(key);
}
}
If no collisions occur, you can always add both directions to the same HashMap :-)
Here my 2 cents.
Or you can use a simple method with generics. Piece of cake.
public static <K,V> Map<V, K> invertMap(Map<K, V> toInvert) {
Map<V, K> result = new HashMap<V, K>();
for(K k: toInvert.keySet()){
result.put(toInvert.get(k), k);
}
return result;
}
Of course you must have a map with unique values. Otherwise, one of them will be replaced.
Inspired by GETah's answer I decided to write something similar by myself with some improvements:
The class is implementing the Map<K,V>-Interface
The bidirectionality is really guaranteed by taking care of it when changing a value by a put (at least I hope to guarantee it hereby)
Usage is just like a normal map, to get a reverse view on the mapping call getReverseView(). The content is not copied, only a view is returned.
I'm not sure this is totally fool-proof (actually, it's probably not), so feel free to comment if you notice any flaws and I'll update the answer.
public class BidirectionalMap<Key, Value> implements Map<Key, Value> {
private final Map<Key, Value> map;
private final Map<Value, Key> revMap;
public BidirectionalMap() {
this(16, 0.75f);
}
public BidirectionalMap(int initialCapacity) {
this(initialCapacity, 0.75f);
}
public BidirectionalMap(int initialCapacity, float loadFactor) {
this.map = new HashMap<>(initialCapacity, loadFactor);
this.revMap = new HashMap<>(initialCapacity, loadFactor);
}
private BidirectionalMap(Map<Key, Value> map, Map<Value, Key> reverseMap) {
this.map = map;
this.revMap = reverseMap;
}
#Override
public void clear() {
map.clear();
revMap.clear();
}
#Override
public boolean containsKey(Object key) {
return map.containsKey(key);
}
#Override
public boolean containsValue(Object value) {
return revMap.containsKey(value);
}
#Override
public Set<java.util.Map.Entry<Key, Value>> entrySet() {
return Collections.unmodifiableSet(map.entrySet());
}
#Override
public boolean isEmpty() {
return map.isEmpty();
}
#Override
public Set<Key> keySet() {
return Collections.unmodifiableSet(map.keySet());
}
#Override
public void putAll(Map<? extends Key, ? extends Value> m) {
m.entrySet().forEach(e -> put(e.getKey(), e.getValue()));
}
#Override
public int size() {
return map.size();
}
#Override
public Collection<Value> values() {
return Collections.unmodifiableCollection(map.values());
}
#Override
public Value get(Object key) {
return map.get(key);
}
#Override
public Value put(Key key, Value value) {
Value v = remove(key);
getReverseView().remove(value);
map.put(key, value);
revMap.put(value, key);
return v;
}
public Map<Value, Key> getReverseView() {
return new BidirectionalMap<>(revMap, map);
}
#Override
public Value remove(Object key) {
if (containsKey(key)) {
Value v = map.remove(key);
revMap.remove(v);
return v;
} else {
return null;
}
}
}
Quite an old question here, but if someone else has brain block like I just did and stumbles on this, hopefully this will help.
I too was looking for a bi-directional HashMap, sometimes it is the simplest of answers that are the most useful.
If you do not wish to re-invent the wheel and prefer not to add other libraries or projects to your project, how about a simple implementation of parallel arrays (or ArrayLists if your design demands it).
SomeType[] keys1 = new SomeType[NUM_PAIRS];
OtherType[] keys2 = new OtherType[NUM_PAIRS];
As soon as you know the index of 1 of the two keys you can easily request the other. So your lookup methods could looks something like:
SomeType getKey1(OtherType ot);
SomeType getKey1ByIndex(int key2Idx);
OtherType getKey2(SomeType st);
OtherType getKey2ByIndex(int key2Idx);
This is assuming you are using proper object oriented structures, where only methods are modifying these arrays/ArrayLists, it would be very simple to keep them parallel. Even easier for an ArrayList since you would not have to rebuild if the size of the arrays change, so long as you add/remove in tandem.

Java collection/map apply method equivalent?

I would like to apply a function to a Java collection, in this particular case a map. Is there a nice way to do this? I have a map and would like to just run trim() on all the values in the map and have the map reflect the updates.
With Java 8's lambdas, this is a one liner:
map.replaceAll((k, v) -> v.trim());
For the sake of history, here's a version without lambdas:
public void trimValues(Map<?, String> map) {
for (Map.Entry<?, String> e : map.entrySet()) {
String val = e.getValue();
if (val != null)
e.setValue(val.trim());
}
}
Or, more generally:
interface Function<T> {
T operate(T val);
}
public static <T> void replaceValues(Map<?, T> map, Function<T> f)
{
for (Map.Entry<?, T> e : map.entrySet())
e.setValue(f.operate(e.getValue()));
}
Util.replaceValues(myMap, new Function<String>() {
public String operate(String val)
{
return (val == null) ? null : val.trim();
}
});
I don't know a way to do that with the JDK libraries other than your accepted response, however Google Collections lets you do the following thing, with the classes com.google.collect.Maps and com.google.common.base.Function:
Map<?,String> trimmedMap = Maps.transformValues(untrimmedMap, new Function<String, String>() {
public String apply(String from) {
if (from != null)
return from.trim();
return null;
}
}
The biggest difference of that method with the proposed one is that it provides a view to your original map, which means that, while it is always in sync with your original map, the apply method could be invoked many times if you are manipulating said map heavily.
A similar Collections2.transform(Collection<F>,Function<F,T>) method exists for collections.
Whether you can modify your collection in-place or not depends on the class of the objects in the collection.
If those objects are immutable (which Strings are) then you can't just take the items from the collection and modify them - instead you'll need to iterate over the collection, call the relevant function, and then put the resulting value back.
Might be overkill for something like this, but there are a number of really good utilities for these types of problems in the Apache Commons Collections library.
Map<String, String> map = new HashMap<String, String>();
map.put("key1", "a ");
map.put("key2", " b ");
map.put("key3", " c");
TransformedMap.decorateTransform(map,
TransformerUtils.nopTransformer(),
TransformerUtils.invokerTransformer("trim"));
I highly recommend the Jakarta Commons Cookbook from O'Reilly.
I ended up using a mutation of #erickson's answer, mutated to:
return a new Collection, not modify in place
return Collections with elements of type equal to the return type of the Function
support mapping over either the values of a map or the elements of a list
Code:
public static interface Function<L, R> {
L operate(R val);
}
public static <K, L, R> Map<K, L> map(Map<K, R> map, Function<L, R> f) {
Map<K, L> retMap = new HashMap<K, L>();
for (Map.Entry<K, R> e : map.entrySet()) retMap.put(e.getKey(), f.operate(e.getValue()));
return retMap;
}
public static <L, R> List<L> map(List<R> list, Function<L, R> f) {
List<L> retList = new ArrayList<L>();
for (R e : list) retList.add(f.operate(e));
return retList;
}
You'll have to iterate over all the entries and trim each String value. Since String is immutable you'll have to re-put it in the map. A better approach might be to trim the values as they're placed in the map.
I have come up with a "Mapper" class
public static abstract class Mapper<FromClass, ToClass> {
private Collection<FromClass> source;
// Mapping methods
public abstract ToClass map(FromClass source);
// Constructors
public Mapper(Collection<FromClass> source) {
this.source = source;
}
public Mapper(FromClass ... source) {
this.source = Arrays.asList(source);
}
// Apply map on every item
public Collection<ToClass> apply() {
ArrayList<ToClass> result = new ArrayList<ToClass>();
for (FromClass item : this.source) {
result.add(this.map(item));
}
return result;
}
}
That I use like that :
Collection<Loader> loaders = new Mapper<File, Loader>(files) {
#Override public Loader map(File source) {
return new Loader(source);
}
}.apply();
You could also take a look at Google Collections

Categories