I'm searching for a fast and convenient way to add save() and rollback() to a standard Map. Let's say i have an object 'table' of class Table which in turn has a private Map called 'rows'. What I'm trying to achieve is a fast and without memory waste method for Row to do something like:
row = new Row();
table.addRow(row).setValue("col1", "foo").setValue("col2", "bar").save();
row.setValue("col2", "beer");
System.out.println(table.getRows()); // 1. col1=foo, col2=bar
row.save();
System.out.println(table.getRows()); // 1. col1=foo, col2=beer
Actually, my design is quite trivial: when addRow() is called, i put() the row inside the map; no buffer, no temp elements; i simply pass the entire Row instance to rows collection. But I need a fast method and (if possible) avoiding the duplication of rows.
any idea?
This sounds too much like "I want to have the new and old values in memory, but I do not want to have the new and old values in memory".
Options:
a) A map of all the added elements, when save do putAll.
b) Your map, instead of <ClassKey, ClassValue>, holds <ClassKey, ClassValue2>. Value2 holds two items of ClassValue, the new and old instance. At save, you pass the new one (if any) to the old one. It will be useful only if you are changing most of the entries in each "transaction".
Not mentioned is the issue of deleting elements, which will bring you yet more joy. With option 2 you can set a boolean at Value2, with option a you will need more workarounds.
What I'm trying to achieve is a fast and without memory waste method
for Row to do something like:
You will waste memory, as you need to keep a "rollback" around somewhere in case it fails along the way. This is how databases handle these types of things.
Now to get the feature you want, you will need to implement your own custom transaction logic, this will allow you to correctly rollback / persist changes to your map. Now within this transaction you will need to keep track of everything that occurred during the transaction. This is because you will be doing temporary writes to your original map, while the transaction processes, subsequently you will need to have the ability to recover from a failed persist/update.
To avoid duplication of rows, the HashMap will already save you from that problem. Assuming you correctly implement a hash function that reports correctly when two objects generate the same code and are therefore "potentially", not definitely, equal in terms of hashing.
Keep two map fields inside custom Map class,
originalMap and temporaryMap.
Read operations are delegated to originalMap, and write operations to temporaryMap.
rollback() shallow-copies originalMap into temporaryMap and commit() vice versa. Since keys and values are never cloned, only the references are kept and memory is not "wasted"
package com.example;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import lombok.NonNull;
import lombok.ToString;
#ToString
public class TransactionalMap<K, V> implements Map<K, V> {
private Map<K, V> originalMap;
private Map<K, V> temporaryMap;
public TransactionalMap() {
this(new HashMap<>());
}
public TransactionalMap(#NonNull Map<K, V> impl) {
if (impl instanceof TransactionalMap) {
throw new IllegalArgumentException("Must provide valid implementation instance");
}
this.originalMap = new HashMap<>(impl);
this.temporaryMap = new HashMap<>(originalMap);
}
#Override
public int size() {
return originalMap.size();
}
#Override
public boolean isEmpty() {
return originalMap.isEmpty();
}
#Override
public boolean containsKey(Object key) {
return originalMap.containsKey(key);
}
#Override
public boolean containsValue(Object value) {
return originalMap.containsValue(value);
}
#Override
public V get(Object key) {
return originalMap.get(key);
}
#Override
public V put(K key, V value) {
return temporaryMap.put(key, value);
}
#Override
public V remove(Object key) {
return temporaryMap.remove(key);
}
#Override
public void putAll(Map<? extends K, ? extends V> m) {
temporaryMap.putAll(m);
}
#Override
public void clear() {
temporaryMap.clear();
}
#Override
public Set<K> keySet() {
return originalMap.keySet();
}
#Override
public Collection<V> values() {
return originalMap.values();
}
#Override
public Set<Entry<K, V>> entrySet() {
return originalMap.entrySet();
}
private void sync(Map<K, V> src, Map<K, V> tgt) {
tgt.putAll(src);
tgt.forEach((k, v) -> {
if (!src.containsKey(k)) tgt.remove(k);
});
}
public void commit() {
sync(temporaryMap, originalMap);
}
public void rollback() {
sync(originalMap, temporaryMap);
}
}
Uses lombok to generate boilerplate code
Related
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();
}
}
}
I would like a Map implementation in which i could add listeners for every time the value in the ArrayList(value part of Map) changes.
HashMap<String, ArrayList<ImageModel>> filePaths = new HashMap<>();
if (!filePaths.containsKey(key) {
filePaths.put(key, new ArrayList<ImageModel>());
}
filePaths.get(key).add(imageModel);//add a listener to this part
After going through this answer and limited knowledge of Observer patterns, I am confused on how to implement this.
Any help would be appreciated.
Thanks
Amir's answer is a pretty good jumping off point for you. However, if you can manipulate the ArrayList outside of the HashMap (which it seems like you'd be able to with this question), you should probably want to wrap the ArrayList. If we tweak his answer a bit, we can get:
public class SpecialArrayList<T> extends ArrayList<T> {
private final Map<K, V> delegatee;
public SpecialArrayList (Map<K, V> delegatee) {
this.delegatee = delegatee;
}
public void add(int i , T value) {
super.add(i, value);
delegatee.actionPerformed/callBack/doTheThing/whatever
}
public void remove (int index) {
super.remove(index);
delegatee.actionPerformed/callBack/doTheThing/whatever
}
// rest of methods here
}
Then just add that actionPerformed() or callBack() or whatever you want to call it in your Map class!
What about a callback? Extend ArrayList and give your own ArrayList the callback (constructor). Override add and remove. In add and remove you have to call super.add() and super.remove() and then callback.onListChanged().
public MyArrayList(MyCallback callback) {
mCallback = callback;
}
public boolean add(E e) {
super.add(e)
callback.onListChanged()
}
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.
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.
Are there any implementations of a static size hashtable that limits the entries to either the most recently or most frequently used metadata? I would prefer not to keep track of this information myself.
I know most caching components keep track of this, but I would rather not introduce quite a lot of new dependencies.
You can build an LRU cache using the standard JDK library using LinkedHashMap:
public class MyLRUCache<K, V> extends LinkedHashMap<K, V> {
private final int maxEntries;
public MyLRUCache(int maxEntries) {
// you can be a bit fancy with capacity and load factor
super(16, 0.75, true);
this.maxEntries = maxEntries;
}
#Override
protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
return size() > maxEntries;
}
}
You may want to play with WeakReferences as well.
Use LinkedHashMap and override removeEldestEntry and make sure to use the
constructor that allows to accessOrder as true
Accessordered makes the map remove the least recently accessed item instead of the eldest.
All queries will alter the structure of the map, hence be a tiny bit slower.
Example:
public AccesOrderedLRUCache<V,K> extends LinkedHashMap<V,K>{
private final m_capacity;
public AccesOrderedLRUCache(int capacity) {
super(0.75*capacity, 0.75, true);
m_capacity = capacity;
}
#Override
protected boolean removeEldestEntry (Map.Entry<K,V> eldest) {
return size() > getCapacity();
}
public int getCapacity() {
return m_capacity;
}
}