I need a map with unique keys and also storing duplicate values only once. The interface will be the Map but I don't want that the same value use memory multiple times. For example:
In a normal Map implementation like HashMap suposing value.equals(value') and value.equals(value'') but value!=value' and value!=value' and value!=value'' if we:
put(key1, value);
put(key2, value');
put(key3, value'');
Then the value will be stored three times.
I tried to make my own implementation which looks like:
class MyMap2<K, V> extends HashMap<K, V> {
private Map<V, V> values;
public MyMap2() {
values = new HashMap<V, V>();
}
#Override
public V put(final K key, final V value) {
V v = values.get(value);
if (v == null) {
v = value;
values.put(v, v);
}
return super.put(key, v);
}
}
This implementation stores the value just one time (Please, note that I'm using the same value). But is there any Map which already implements this kind of data structure with get/put O(1)?
Please, note that BiMap is not useful because it will cause an error in case of duplicated values.
This implementation already promises constant time get/put operations. The worst case is when inserting a new value that has never been seen yet. In this case you will:
Attempt to find the value in the values map - O(1), since it's a HashMap.
Not find it, and put the value in the values map - O(1), since it's a HashMap.
Put the key-value pair in super - O(1), since it's a HashMap.
You find a better way of implementing this logic, but not by an order of magnitude.
EDIT:
Note that the implementation may put in super twice, which is just redundant. It can be tweaked to be slightly cleaner:
#Override
public V put(final K key, final V value) {
V v = values.get(value);
if (v == null) {
v = value
values.put(v, v);
}
return super.put(key, v);
}
This was already asked before.
Check out BiMap.
Related
I have a arraylist which contains these values:
ArrayList<string,string> list= new ArrayList<>();
list.put("A101","2020_1.0");
list.put("A101","2020_3.0");
list.put("A101","2020_2.0");
list.put("A102","2020_2.0");
list.put("A102","2020_1.0");
I need to get:
A101 2020_3.0 and
A102 2020_2.0
First, the data container here does not appear to be a List<String>, it is rather a Map<String, String> because List does not have method put(K key, V value); while Map does.
However, if method Map::put is used, it overwrites previous values with the new ones. So, a custom map may be implemented with the overridden method put (as well as putAll) to compare the values and keep the one with the "highest" version.
class MyMap<K, V> extends LinkedHashMap<K, V> {
private final Comparator<V> valueComparator;
public MyMap(Comparator<V> valueComparator) {
super();
this.valueComparator = valueComparator;
}
#Override
public V put(K key, V value) {
return compute(key,
(k, v) -> v == null ? value // value missing
: valueComparator.compare(v, value) < 0
? value : v // keep the max of two
);
}
#Override
public void putAll(Map<? extends K, ? extends V> m) {
m.forEach(this::put);
}
}
Assuming that in the existing examples the values can be compared as usual Strings, the map may be created as follows:
// use plain String comparison
Map<String, String> data = new MyMap<>(String::compareTo);
data.put("A101", "2020_1.0");
data.put("A101", "2020_3.0");
data.put("A101", "2020_2.0");
data.put("A102", "2020_2.0");
data.put("A102", "2020_1.0");
System.out.println(data);
Output:
{A101=2020_3.0, A102=2020_2.0}
If the "version" values have more complex comparison rules (e.g. split to integer parts as in 9.1 and 10.0, exclude some prefix/suffix, etc.), a custom comparator needs to be implemented.
Basically, I need something like a TreeMap but that would allow me to get the element at the position X efficiently.
You can use a ListOrderedMap from Apache Commons Collections.
It gives you a get(int index) method to retrieve the key at position index on top of the usual Map methods.
A balanced tree can be used for both lookups by key and by index, both in O(log N) time, if you store a "size" field in each node which tracks how many key/value pairs are contained in the node and all its descendants.
The code for looking up a value by index would look something like this (in pseudocode):
def at(index)
if index == this.left.size
return this.value
else if index < this.left.size
return this.left.at(index)
else
return this.right.at(index - this.left.size - 1)
TreeMap get complexity is O(log n)
I guess it is not a concern in performance.
Also as i know array has O(1) but the your demands match with treemap.
The SortedMap Interface with TreeMap is suitable for you :).
It wouldn't be difficult to combine some data structures to provide this. Assuming there are no duplicates then the sketch below could work. If you do need to support duplicates then wrap your objects with something that provides unique hashes like the default Object class does.
I have no idea how you want use the positional data so I didn't add any methods that relate to reordering or iterating but it wouldn't be difficult. Sounds like the Apaches ListOrderedMap is a good choice too.
public class OrderedMap<K, V>{
private ArrayList<V> values;
private HashMap<K, V> map;
private HashMap<K, Integer> keysToIndices;
public OrderedMap(){
values = new ArrayList<>();
map = new HashMap<>();
keysToIndices = new HashMap<>();
}
public void put(K key, V value){
values.add(value);
map.put(key, value);
keysToIndices.put(key, values.size()-1);
}
public T get(K key){
return map.get(key);
}
public V remove(K key){
map.remove(key);
return values.remove(keysToIndices.remove(key))
}
}
I need a Map<String,Integer> to store a binding between a set o strings and ints, for example:
"one" <-> 1
"two" <-> 2
"three" <-> 3
and in particular I need to use both String values and int values as key to access this map. I mean: get("one") returns 1 and get(1) returns "one".
What's the best way to achieve this? is there some Map implementation that can help me?
Either use two HashMaps and write a method to query one of the two depending on what input you are giving it (String or int) or use the Guava library's HashBiMap, which does something like that behind the scenes for you.
Could possible create an inverted map on demand. This wont support same value for two keys.
public class InvertableMap<K, V> extends HashMap<K, V> {
public InvertableMap<V, K> getInvertedMap() {
InvertableMap<V, K> outputMap = new InvertableMap<>();
for (K k : keySet()) {
V v = get(k);
outputMap.put(v, k);
}
return outputMap;
}
}
I need to have an automatically sorted-by-values map in Java - so that It keeps being sorted at any time while I'm adding new key-value pairs or update the value of an existing key-value pair, or even delete some entry.
Please also have in mind that this map is going to be really big (100's of thousands, or even 10's of millions of entries in size).
So basically I'm looking for the following functionality:
Supposed that we had a class 'SortedByValuesMap' that implements the aforementioned functionality
and we have the following code:
SortedByValuesMap<String,Long> sorted_map = new SortedByValuesMap<String, Long>();
sorted_map.put("apples", 4);
sorted_map.put("oranges", 2);
sorted_map.put("bananas", 1);
sorted_map.put("lemons", 3);
sorted_map.put("bananas", 6);
for (String key : sorted_map.keySet()) {
System.out.println(key + ":" + sorted_map.get(key));
}
the output should be:
bananas:6
apples:4
lemons:3
oranges:2
In particular, what's really important for me, is to be able to get the entry with the
lowest value at any time - using a command like:
smallestItem = sorted_map.lastEntry();
which should give me the 'oranges' entry
EDIT: I am a Java newbie so please elaborate a bit in your answers - thanks
EDIT2: This might help: I am using this for counting words (for those who are familiar: n-grams in particular) in huge text files. So I need to build a map where keys are words and values are the frequencies of those words. However, due to limitations (like RAM), I want to keep only the X most frequent words - but you can't know beforehand which are going to be the most frequent words of course. So, the way I thought it might work (as an approximation) is to start counting words and when the map reaches a top-limit (like 1 mil entries) , the least frequent entry will be deleted so as to keep the map's size to 1 mil always.
Keep 2 data structures:
A dictionary of words -> count. Just use an ordinary HashMap<String, Long>.
An "array" to keep track of order, such that list[count] holds a Set<String> of words with that count.
I'm writing this as though it were an array as a notational convenience. In fact, you probably don't know an upper bound on the number of occurrences, so you need a resizable data structure. Implement using a Map<Long, Set<String>>. Or, if that uses too much memory, use an ArrayList<Set<String>> (you'll have to test for count == size() - 1, and if so, use add() instead of set(count + 1)).
To increment the number of occurrences for a word (pseudocode):
// assumes data structures are in instance variables dict and arr
public void tally(final String word)
{
final long count = this.dict.get(word) or 0 if absent;
this.dict.put(word, count + 1);
// move word up one place in arr
this.arr[count].remove(word); // This is why we use a Set: for fast deletion here.
this.arr[count + 1].add(word);
}
To iterate over words in order (pseudocode):
for(int count = 0; count < arr.size; count++)
for(final String word : this.arr[count])
process(word, count);
How about using additional index or only TreeMap<Long, TreeSet<String>> or TreeMap<Long, String> if Long values are distinct?
You can also write a Heap.
Guava BiMap Solution:
//Prepare original data
BiMap<String, Integer> biMap = HashBiMap.create();
biMap.put("apples" , 4);
biMap.put("oranges", 2);
biMap.put("bananas", 1);
biMap.put("lemons" , 3);
biMap.put("bananas", 6);
//Create a desc order SortedMap
SortedMap<Integer, String> sortedMap = new TreeMap<Integer, String>(new Comparator<Integer>(){
#Override public int compare(Integer o1, Integer o2) {
return o2-o1;
}});
//Put inversed map
sortedMap.putAll(biMap.inverse());
for (Map.Entry<Integer, String> e: sortedMap.entrySet()) {
System.out.println(e);
}
System.out.println(sortedMap.lastKey());
Try the solution posted on http://paaloliver.wordpress.com/2006/01/24/sorting-maps-in-java/ . You have the flexibility of doing sorting ascending or descending too.
Here is what they say
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;
public class MapValueSort {
/** inner class to do soring of the map **/
private static class ValueComparer implements Comparator<String> {
private Map<String, String> _data = null;
public ValueComparer (Map<String, String> data){
super();
_data = data;
}
public int compare(String o1, String o2) {
String e1 = (String) _data.get(o1);
String e2 = (String) _data.get(o2);
return e1.compareTo(e2);
}
}
public static void main(String[] args){
Map<String, String> unsortedData = new HashMap<String, String>();
unsortedData.put("2", "DEF");
unsortedData.put("1", "ABC");
unsortedData.put("4", "ZXY");
unsortedData.put("3", "BCD");
SortedMap<String, String> sortedData = new TreeMap<String, String>(new MapValueSort.ValueComparer(unsortedData));
printMap(unsortedData);
sortedData.putAll(unsortedData);
System.out.println();
printMap(sortedData);
}
private static void printMap(Map<String, String> data) {
for (Iterator<String> iter = data.keySet().iterator(); iter.hasNext();) {
String key = (String) iter.next();
System.out.println("Value/key:"+data.get(key)+"/"+key);
}
}
}
Outputs
Value/key:BCD/3
Value/key:DEF/2
Value/key:ABC/1
Value/key:ZXY/4
Value/key:ABC/1
Value/key:BCD/3
Value/key:DEF/2
Value/key:ZXY/4
I found the need of a similar structure to keep a list of objects ordered by associated values. Based on the suggestion from Mechanical snail in this thread, I coded up a basic implementation of such a map. Feel free to use.
import java.util.*;
/**
* A map where {#link #keySet()} and {#link #entrySet()} return sets ordered
* with ascending associated values with respect to the the comparator provided
* at constuction. The order of two or more keys with identical values is not
* defined.
* <p>
* Several contracts of the Map interface are not satisfied by this minimal
* implementation.
*/
public class ValueSortedMap<K, V> extends HashMap<K, V> {
protected Map<V, Collection<K>> valueToKeysMap;
public ValueSortedMap() {
this((Comparator<? super V>) null);
}
public ValueSortedMap(Comparator<? super V> valueComparator) {
this.valueToKeysMap = new TreeMap<V, Collection<K>>(valueComparator);
}
public boolean containsValue(Object o) {
return valueToKeysMap.containsKey(o);
}
public V put(K k, V v) {
V oldV = null;
if (containsKey(k)) {
oldV = get(k);
valueToKeysMap.get(oldV).remove(k);
}
super.put(k, v);
if (!valueToKeysMap.containsKey(v)) {
Collection<K> keys = new ArrayList<K>();
keys.add(k);
valueToKeysMap.put(v, keys);
} else {
valueToKeysMap.get(v).add(k);
}
return oldV;
}
public void putAll(Map<? extends K, ? extends V> m) {
for (Map.Entry<? extends K, ? extends V> e : m.entrySet())
put(e.getKey(), e.getValue());
}
public V remove(Object k) {
V oldV = null;
if (containsKey(k)) {
oldV = get(k);
super.remove(k);
valueToKeysMap.get(oldV).remove(k);
}
return oldV;
}
public void clear() {
super.clear();
valueToKeysMap.clear();
}
public Set<K> keySet() {
LinkedHashSet<K> ret = new LinkedHashSet<K>(size());
for (V v : valueToKeysMap.keySet()) {
Collection<K> keys = valueToKeysMap.get(v);
ret.addAll(keys);
}
return ret;
}
public Set<Map.Entry<K, V>> entrySet() {
LinkedHashSet<Map.Entry<K, V>> ret = new LinkedHashSet<Map.Entry<K, V>>(size());
for (Collection<K> keys : valueToKeysMap.values()) {
for (final K k : keys) {
final V v = get(k);
ret.add(new Map.Entry<K,V>() {
public K getKey() {
return k;
}
public V getValue() {
return v;
}
public V setValue(V v) {
throw new UnsupportedOperationException();
}
});
}
}
return ret;
}
}
This implementation does not honor all the contracts of the Map interface such as reflecting value changes and removals in the returned key set and entry sets in the actual map, but such a solution would be a bit large to include in a forum like this. Perhaps I will work on one and make it available via github or something similar.
Update: You cannot sort maps by values, sorry.
You can use SortedMap implementation like TreeMap with Comparator defining order by values (instead of default - by keys).
Or, even better, you can put elements into a PriorityQueue with predefined comparator by values. It should be faster and take less memory compared to TreeMap.
You may refer to the implementation of java.util.LinkedHashMap.
The basic idea is, using a inner linked list to store orders. Here is some details:
Extends from HashMap. In HashMap, each entry has a key and value, that is basic. You can Add a next and a prev pointer to store entries in order by value. And a header and tail pointer to get the first and last entry. For every modification (add, remove, update), you can add your own code to change the list order. It is no more than a linear search and pointer switch.
Sure it will be slow for add/update if there are too many entries because it is a linked list not array. But as long as the list is sorted, I believe there are lots of ways to speedup the search.
So here is what you got: A map that has the same speed with HashMap when retrieving an entry by a key. A linked list which stores entries in order.
We can discuss this further if this solution meets your requirement.
to jtahlborn:
As I said, it surely is slow without any optimization. Since we are talking about performance not impl now, lots of things can be done.
One solution is using a tree instead of Linked List, like Red-Black Tree. Then iterate the tree instead of iterator the map.
About the smallest value, it is easier. Just using a member variable to store the smallest, when add or update an element, update the smallest value. When delete, search the tree for the smallest (this is very fast)
if tree is too complex, it is also possible to using another list/array to mark the some positions in the list. for example, maybe 100 element each. Then when search, just search the position list first and then the real list. This list also needs to be maintained, it would be reasonable to recount the position list for certain times of modification, maybe 100.
if all you need is the "min" value, then just use a normal map and keep track of the "min" value anytime it is modified.
EDIT:
so, if you really need value ordering and you want to use out-of-the-box solutions, you basically need 2 collections. One normal map (e.g. HashMap), and one SortedSet (e.g. TreeSet>). you can traverse ordered elements via the TreeSet, and find frequencies by key using the HashMap.
obviously, you could always code up something yourself sort of like a LinkedHashMap, where the elements are locatable by key and traversable by order, but that's pretty much going to be entirely custom code (i doubt anything that specific already exists, but i could be wrong).
I'm looking for a class in java that has key-value association, but without using hashes. Here is what I'm currently doing:
Add values to a Hashtable.
Get an iterator for the Hashtable.entrySet().
Iterate through all values and:
Get a Map.Entry for the iterator.
Create an object of type Module (a custom class) based on the value.
Add the class to a JPanel.
Show the panel.
The problem with this is that I do not have control over the order that I get the values back, so I cannot display the values in the a given order (without hard-coding the order).
I would use an ArrayList or Vector for this, but later in the code I need to grab the Module object for a given Key, which I can't do with an ArrayList or Vector.
Does anyone know of a free/open-source Java class that will do this, or a way to get values out of a Hashtable based on when they were added?
Thanks!
I suggest a LinkedHashMap or a TreeMap. A LinkedHashMap keeps the keys in the order they were inserted, while a TreeMap is kept sorted via a Comparator or the natural Comparable ordering of the keys.
Since it doesn't have to keep the elements sorted, LinkedHashMap should be faster for most cases; TreeMap has O(log n) performance for containsKey, get, put, and remove, according to the Javadocs, while LinkedHashMap is O(1) for each.
If your API that only expects a predictable sort order, as opposed to a specific sort order, consider using the interfaces these two classes implement, NavigableMap or SortedMap. This will allow you not to leak specific implementations into your API and switch to either of those specific classes or a completely different implementation at will afterwards.
LinkedHashMap will return the elements in the order they were inserted into the map when you iterate over the keySet(), entrySet() or values() of the map.
Map<String, String> map = new LinkedHashMap<String, String>();
map.put("id", "1");
map.put("name", "rohan");
map.put("age", "26");
for (Map.Entry<String, String> entry : map.entrySet()) {
System.out.println(entry.getKey() + " = " + entry.getValue());
}
This will print the elements in the order they were put into the map:
id = 1
name = rohan
age = 26
If an immutable map fits your needs then there is a library by google called guava (see also guava questions)
Guava provides an ImmutableMap with reliable user-specified iteration order. This ImmutableMap has O(1) performance for containsKey, get. Obviously put and remove are not supported.
ImmutableMap objects are constructed by using either the elegant static convenience methods of() and copyOf() or a Builder object.
You can use LinkedHashMap to main insertion order in Map
The important points about Java LinkedHashMap class are:
It contains only unique elements.
A LinkedHashMap contains values based on the key.
It may have one null key and multiple null values.
It is same as HashMap instead maintains insertion order
public class LinkedHashMap<K,V> extends HashMap<K,V> implements Map<K,V>
But if you want sort values in map using User-defined object or any primitive data type key then you should use TreeMap For more information, refer this link
You can maintain a Map (for fast lookup) and List (for order) but a LinkedHashMap may be the simplest. You can also try a SortedMap e.g. TreeMap, which an have any order you specify.
Either You can use LinkedHashMap<K, V> or you can implement you own CustomMap which maintains insertion order.
You can use the Following CustomHashMap with the following features:
Insertion order is maintained, by using LinkedHashMap internally.
Keys with null or empty strings are not allowed.
Once key with value is created, we are not overriding its value.
HashMap vs LinkedHashMap vs CustomHashMap
interface CustomMap<K, V> extends Map<K, V> {
public boolean insertionRule(K key, V value);
}
#SuppressWarnings({ "rawtypes", "unchecked" })
public class CustomHashMap<K, V> implements CustomMap<K, V> {
private Map<K, V> entryMap;
// SET: Adds the specified element to this set if it is not already present.
private Set<K> entrySet;
public CustomHashMap() {
super();
entryMap = new LinkedHashMap<K, V>();
entrySet = new HashSet();
}
#Override
public boolean insertionRule(K key, V value) {
// KEY as null and EMPTY String is not allowed.
if (key == null || (key instanceof String && ((String) key).trim().equals("") ) ) {
return false;
}
// If key already available then, we are not overriding its value.
if (entrySet.contains(key)) { // Then override its value, but we are not allowing
return false;
} else { // Add the entry
entrySet.add(key);
entryMap.put(key, value);
return true;
}
}
public V put(K key, V value) {
V oldValue = entryMap.get(key);
insertionRule(key, value);
return oldValue;
}
public void putAll(Map<? extends K, ? extends V> t) {
for (Iterator i = t.keySet().iterator(); i.hasNext();) {
K key = (K) i.next();
insertionRule(key, t.get(key));
}
}
public void clear() {
entryMap.clear();
entrySet.clear();
}
public boolean containsKey(Object key) {
return entryMap.containsKey(key);
}
public boolean containsValue(Object value) {
return entryMap.containsValue(value);
}
public Set entrySet() {
return entryMap.entrySet();
}
public boolean equals(Object o) {
return entryMap.equals(o);
}
public V get(Object key) {
return entryMap.get(key);
}
public int hashCode() {
return entryMap.hashCode();
}
public boolean isEmpty() {
return entryMap.isEmpty();
}
public Set keySet() {
return entrySet;
}
public V remove(Object key) {
entrySet.remove(key);
return entryMap.remove(key);
}
public int size() {
return entryMap.size();
}
public Collection values() {
return entryMap.values();
}
}
Usage of CustomHashMap:
public static void main(String[] args) {
System.out.println("== LinkedHashMap ==");
Map<Object, String> map2 = new LinkedHashMap<Object, String>();
addData(map2);
System.out.println("== CustomHashMap ==");
Map<Object, String> map = new CustomHashMap<Object, String>();
addData(map);
}
public static void addData(Map<Object, String> map) {
map.put(null, "1");
map.put("name", "Yash");
map.put("1", "1 - Str");
map.put("1", "2 - Str"); // Overriding value
map.put("", "1"); // Empty String
map.put(" ", "1"); // Empty String
map.put(1, "Int");
map.put(null, "2"); // Null
for (Map.Entry<Object, String> entry : map.entrySet()) {
System.out.println(entry.getKey() + " = " + entry.getValue());
}
}
O/P:
== LinkedHashMap == | == CustomHashMap ==
null = 2 | name = Yash
name = Yash | 1 = 1 - Str
1 = 2 - Str | 1 = Int
= 1 |
= 1 |
1 = Int |
If you know the KEY's are fixed then you can use EnumMap. Get the values form Properties/XML files
EX:
enum ORACLE {
IP, URL, USER_NAME, PASSWORD, DB_Name;
}
EnumMap<ORACLE, String> props = new EnumMap<ORACLE, String>(ORACLE.class);
props.put(ORACLE.IP, "127.0.0.1");
props.put(ORACLE.URL, "...");
props.put(ORACLE.USER_NAME, "Scott");
props.put(ORACLE.PASSWORD, "Tiget");
props.put(ORACLE.DB_Name, "MyDB");
I don't know if it is opensource, but after a little googling, I found this implementation of Map using ArrayList. It seems to be pre-1.5 Java, so you might want to genericize it, which should be easy. Note that this implementation has O(N) access, but this shouldn't be a problem if you don't add hundreds of widgets to your JPanel, which you shouldn't anyway.
Whenever i need to maintain the natural order of things that are known ahead of time, i use a EnumMap
the keys will be enums and you can insert in any order you want but when you iterate it will iterate in the enum order (the natural order).
Also when using EnumMap there should be no collisions which can be more efficient.
I really find that using enumMap makes for clean readable code.
Here is an example