Removing from multiMap - java

In school we are to implement our own class MultiMap.
When I were implementing the remove method, I ran in to some trouble.
My problem is when I call the remove method, the set.Remove(value) returns false. Just like if the set didn't contain the object.
I've tried to write out the object refferences from the main, and the object reference from the MultiMap class, and the objects seems to be the same.
What am I missing here?
Thanks in advance
Here's my map code :
public class MultiMap<K, V> {
private final Map<K, Set<V>> map = new HashMap<>();
public MultiMap() {
}
public String toString() {
StringBuilder sb = new StringBuilder();
Set<K> keys = map.keySet();
for (K k : keys) {
sb.append("key ");
sb.append(k);
sb.append(" Value ");
sb.append(map.get(k));
}
return sb.toString();
}
public int size() {
return map.size();
}
public boolean put(K key, V value) {
Set<V> set;
if (map.containsKey(key)) {
set = map.get(key);
set.add(value);
} else {
set = new HashSet<>();
set.add(value);
}
return (map.put(key, set) != null) ? false : true;
}
public Set<V> get(K key) {
return map.get(key);
}
public void remove(K key, V value) {
Set<V> set = map.get(key);
System.out.println(value);
System.out.println(set.remove(value));
if(set.isEmpty()) {
map.remove(key);
}
}
Main:
public static void main(String[] args) {
Person p = new Person("navn");
Collection<Person> set = new HashSet<>();
set.add(p);
MultiMap map = new MultiMap<>();
map.put(1, set);
System.out.println(map.toString());
System.out.println(map.get(1));
map.remove(1, p);
}

The first problem is in your put() method. You shall not create a new HashSet every time you insert a new element.
You should first check whether your underlying map contains the provided key, by using containsKey(). If the key exists, just add the new value. Otherwise, create a new HashSet and keep your code.
Then, you will have to fix your remove() method. It will raise a NullPointerException if the key does not exists. Indeed, map.get(key) will return null and set.remove() will fail.
EDIT:
See #Eugen Halca answer about your Multimap usage. In your main() method, you are adding a Collection of Person, but trying to remove a single Person. Even with the best Multimap implementation, that won't work ;)

you remove returns false because you are trying to remove Person object, but you don't have it in the map.
Notice you are adding Collection<Person> to map, but trying to remove Person, obviously those 2 objects are not equal, therefore you get false value.
after : map.put(1, set); in memory you have something like :
1=>[[p]]; but ,i guess, you expected 1=>[p].
just replace map.put(1,set); with map.put(1,p);
Also take a look at Guillaume Poussel answer for proper MultiMap implementation advice.

Related

Java Map - log message when key is not found in getOrDefault

I have a Map<String, List<SomeClass>> someMap and I'm retrieving the value based on someKey and for each element of the list of SomeClass I'm performing other operations.
someMap.getOrDefault(someKey, new ArrayList<>()).forEach(...)
I also want to be able to log messages when I don't find someKey. How would I be able to achieve it optimally? Is there any other function/way to achieve this behavior?
Map<String, List<String>> map = new HashMap<>();
List<String> l = new ArrayList<>();
l.add("b");
map.put("a", l);
Yes, you can do it in a single statement. Use .compute().
map.compute("a", (k, v) -> {
if (v == null) {
System.out.println("Key Not Found");
return new ArrayList<>();
}
return v;
}).forEach(System.out::println);
There's also computeIfAbsent() which will only compute the lambda if the key is not present.
Note, from the documentation:
Attempts to compute a mapping for the specified key and its current
mapped value (or null if there is no current mapping).
This will add the key which was not found in your map.
If you want to remove those keys later, then simply add those keys to a list inside the if and remove them in one statement like this:
map.keySet().removeAll(listToRemove);
You can create a function to do that. For example, I created a function which will get the value from the map, return it if it is not null, or an empty list otherwise. Before returning the empty list, you can run a Runnable action. The main benefit of that is that you can do more than just logging there.
#Slf4j
public class Main {
public static Collection<String> retrieveOrRun(Map<String, Collection<String>> map, String key, Runnable runnable) {
final Collection<String> strings = map.get(key);
if (strings == null) {
runnable.run();
return Collections.emptyList();
} else {
return strings;
}
}
public static void main(String[] args) {
Map<String, Collection<String>> map = new HashMap<>();
Collection<String> strings = retrieveOrRun(map, "hello", () -> log.warn("Could not find a value for the key : {}", "hello"));
}
}
I think you have two choices:
Either you use a wrapper method, doing the actual call (getOrDefault, etc) and handling missing keys.
public static <K,V> V getOrDefault(Map<K,V> map, K key, V defaultValue) {
V value = map.get(key);
if (value == null) {
logMissingValue(key);
return defaultValue;
}
return value;
}
Or you create new implementation of Map doing just that, with a delegation to method that should be delegated (I won't do here in this example, but Eclipse work pretty well: Alt + Shift + S > Create delegate methods):
class LoggerMap<K,V> implements Map<K,V> {
private final Map<K,V> internal;
public LoggerMap(Map<K,V> internal) {
this.internal = Objects.requireNonNull(internal, "internal");
}
#Override
public V getOrDefault(K key, V defaultValue) {
... if not found logMissingValue(key); ...
}
}
Now about which is optimal, that depends on your needs: if you know you will always use the wrapper method, then your missing keys will always be logged. Creating a new map implementation would be overkill.
If your need is to log absolutely all missing keys - even if foreign code (for example, some API taking a map as a parameter), then your best choice is a map implementation:
In terms of performance, I don't think you should worry about delegation: I did not test it using a benchmark, but the JVM should be able to optimize that.
There are other parts where a key might return a missing value (eg: remove, get, ...), using such an implementation will allow you to easily trace those as well.

Get the key from a value in map Java [duplicate]

If I have the value "foo", and a HashMap<String> ftw for which ftw.containsValue("foo") returns true, how can I get the corresponding key? Do I have to loop through the hashmap? What is the best way to do that?
If your data structure has many-to-one mapping between keys and values you should iterate over entries and pick all suitable keys:
public static <T, E> Set<T> getKeysByValue(Map<T, E> map, E value) {
Set<T> keys = new HashSet<T>();
for (Entry<T, E> entry : map.entrySet()) {
if (Objects.equals(value, entry.getValue())) {
keys.add(entry.getKey());
}
}
return keys;
}
In case of one-to-one relationship, you can return the first matched key:
public static <T, E> T getKeyByValue(Map<T, E> map, E value) {
for (Entry<T, E> entry : map.entrySet()) {
if (Objects.equals(value, entry.getValue())) {
return entry.getKey();
}
}
return null;
}
In Java 8:
public static <T, E> Set<T> getKeysByValue(Map<T, E> map, E value) {
return map.entrySet()
.stream()
.filter(entry -> Objects.equals(entry.getValue(), value))
.map(Map.Entry::getKey)
.collect(Collectors.toSet());
}
Also, for Guava users, BiMap may be useful. For example:
BiMap<Token, Character> tokenToChar =
ImmutableBiMap.of(Token.LEFT_BRACKET, '[', Token.LEFT_PARENTHESIS, '(');
Token token = tokenToChar.inverse().get('(');
Character c = tokenToChar.get(token);
If you choose to use the Commons Collections library instead of the standard Java Collections framework, you can achieve this with ease.
The BidiMap interface in the Collections library is a bi-directional map, allowing you to map a key to a value (like normal maps), and also to map a value to a key, thus allowing you to perform lookups in both directions. Obtaining a key for a value is supported by the getKey() method.
There is a caveat though, bidi maps cannot have multiple values mapped to keys, and hence unless your data set has 1:1 mappings between keys and values, you cannot use bidi maps.
If you want to rely on the Java Collections API, you will have to ensure the 1:1 relationship between keys and values at the time of inserting the value into the map. This is easier said than done.
Once you can ensure that, use the entrySet() method to obtain the set of entries (mappings) in the Map. Once you have obtained the set whose type is Map.Entry, iterate through the entries, comparing the stored value against the expected, and obtain the corresponding key.
Support for bidi maps with generics can be found in Google Guava and the refactored Commons-Collections libraries (the latter is not an Apache project). Thanks to Esko for pointing out the missing generic support in Apache Commons Collections. Using collections with generics makes more maintainable code.
Since version 4.0 the official Apache Commons Collections™ library supports generics.
See the summary page of the "org.apache.commons.collections4.bidimap" package for the list of available implementations of the BidiMap, OrderedBidiMap and SortedBidiMap interfaces that now support Java generics.
public class NewClass1 {
public static void main(String[] args) {
Map<Integer, String> testMap = new HashMap<Integer, String>();
testMap.put(10, "a");
testMap.put(20, "b");
testMap.put(30, "c");
testMap.put(40, "d");
for (Entry<Integer, String> entry : testMap.entrySet()) {
if (entry.getValue().equals("c")) {
System.out.println(entry.getKey());
}
}
}
}
Some additional info... May be useful to you
Above method may not be good if your hashmap is really big. If your hashmap contain unique key to unique value mapping, you can maintain one more hashmap that contain mapping from Value to Key.
That is you have to maintain two hashmaps
1. Key to value
2. Value to key
In that case you can use second hashmap to get key.
You could insert both the key,value pair and its inverse into your map structure
map.put("theKey", "theValue");
map.put("theValue", "theKey");
Using map.get("theValue") will then return "theKey".
It's a quick and dirty way that I've made constant maps, which will only work for a select few datasets:
Contains only 1 to 1 pairs
Set of values is disjoint from the set of keys (1->2, 2->3 breaks it)
I think your choices are
Use a map implementation built for this, like the BiMap from google collections. Note that the google collections BiMap requires uniqueless of values, as well as keys, but it provides high performance in both directions performance
Manually maintain two maps - one for key -> value, and another map for value -> key
Iterate through the entrySet() and to find the keys which match the value. This is the slowest method, since it requires iterating through the entire collection, while the other two methods don't require that.
Using Java 8:
ftw.forEach((key, value) -> {
if (value.equals("foo")) {
System.out.print(key);
}
});
Decorate map with your own implementation
class MyMap<K,V> extends HashMap<K, V>{
Map<V,K> reverseMap = new HashMap<V,K>();
#Override
public V put(K key, V value) {
// TODO Auto-generated method stub
reverseMap.put(value, key);
return super.put(key, value);
}
public K getKey(V value){
return reverseMap.get(value);
}
}
There is no unambiguous answer, because multiple keys can map to the same value. If you are enforcing unique-ness with your own code, the best solution is to create a class that uses two Hashmaps to track the mappings in both directions.
If you build the map in your own code, try putting the key and value in the map together:
public class KeyValue {
public Object key;
public Object value;
public KeyValue(Object key, Object value) { ... }
}
map.put(key, new KeyValue(key, value));
Then when you have a value, you also have the key.
I think this is best solution, original address: Java2s
import java.util.HashMap;
import java.util.Map;
public class Main {
public static void main(String[] argv) {
Map<String, String> map = new HashMap<String, String>();
map.put("1","one");
map.put("2","two");
map.put("3","three");
map.put("4","four");
System.out.println(getKeyFromValue(map,"three"));
}
// hm is the map you are trying to get value from it
public static Object getKeyFromValue(Map hm, Object value) {
for (Object o : hm.keySet()) {
if (hm.get(o).equals(value)) {
return o;
}
}
return null;
}
}
An easy usage:
if you put all data in hasMap and you have item = "Automobile", so you are looking its key in hashMap. that is good solution.
getKeyFromValue(hashMap, item);
System.out.println("getKeyFromValue(hashMap, item): "+getKeyFromValue(hashMap, item));
To find all the keys that map to that value, iterate through all the pairs in the hashmap, using map.entrySet().
I'm afraid you'll just have to iterate your map. Shortest I could come up with:
Iterator<Map.Entry<String,String>> iter = map.entrySet().iterator();
while (iter.hasNext()) {
Map.Entry<String,String> entry = iter.next();
if (entry.getValue().equals(value_you_look_for)) {
String key_you_look_for = entry.getKey();
}
}
for(int key: hm.keySet()) {
if(hm.get(key).equals(value)) {
System.out.println(key);
}
}
It sounds like the best way is for you to iterate over entries using map.entrySet() since map.containsValue() probably does this anyway.
For Android development targeting API < 19, Vitalii Fedorenko one-to-one relationship solution doesn't work because Objects.equals isn't implemented. Here's a simple alternative:
public <K, V> K getKeyByValue(Map<K, V> map, V value) {
for (Map.Entry<K, V> entry : map.entrySet()) {
if (value.equals(entry.getValue())) {
return entry.getKey();
}
}
return null;
}
You can use the below:
public class HashmapKeyExist {
public static void main(String[] args) {
HashMap<String, String> hmap = new HashMap<String, String>();
hmap.put("1", "Bala");
hmap.put("2", "Test");
Boolean cantain = hmap.containsValue("Bala");
if(hmap.containsKey("2") && hmap.containsValue("Test"))
{
System.out.println("Yes");
}
if(cantain == true)
{
System.out.println("Yes");
}
Set setkeys = hmap.keySet();
Iterator it = setkeys.iterator();
while(it.hasNext())
{
String key = (String) it.next();
if (hmap.get(key).equals("Bala"))
{
System.out.println(key);
}
}
}
}
I think keySet() may be well to find the keys mapping to the value, and have a better coding style than entrySet().
Ex:
Suppose you have a HashMap map, ArrayList res, a value you want to find all the key mapping to , then store keys to the res.
You can write code below:
for (int key : map.keySet()) {
if (map.get(key) == value) {
res.add(key);
}
}
rather than use entrySet() below:
for (Map.Entry s : map.entrySet()) {
if ((int)s.getValue() == value) {
res.add((int)s.getKey());
}
}
Hope it helps :)
Yes, you have to loop through the hashmap, unless you implement something along the lines of what these various answers suggest. Rather than fiddling with the entrySet, I'd just get the keySet(), iterate over that set, and keep the (first) key that gets you your matching value. If you need all the keys that match that value, obviously you have to do the whole thing.
As Jonas suggests, this might already be what the containsValue method is doing, so you might just skip that test all-together, and just do the iteration every time (or maybe the compiler will already eliminate the redundancy, who knows).
Also, relative to the other answers, if your reverse map looks like
Map<Value, Set<Key>>
you can deal with non-unique key->value mappings, if you need that capability (untangling them aside). That would incorporate fine into any of the solutions people suggest here using two maps.
You can get the key using values using following code..
ArrayList valuesList = new ArrayList();
Set keySet = initalMap.keySet();
ArrayList keyList = new ArrayList(keySet);
for(int i = 0 ; i < keyList.size() ; i++ ) {
valuesList.add(initalMap.get(keyList.get(i)));
}
Collections.sort(valuesList);
Map finalMap = new TreeMap();
for(int i = 0 ; i < valuesList.size() ; i++ ) {
String value = (String) valuesList.get(i);
for( int j = 0 ; j < keyList.size() ; j++ ) {
if(initalMap.get(keyList.get(j)).equals(value)) {
finalMap.put(keyList.get(j),value);
}
}
}
System.out.println("fianl map ----------------------> " + finalMap);
public static class SmartHashMap <T1 extends Object, T2 extends Object> {
public HashMap<T1, T2> keyValue;
public HashMap<T2, T1> valueKey;
public SmartHashMap(){
this.keyValue = new HashMap<T1, T2>();
this.valueKey = new HashMap<T2, T1>();
}
public void add(T1 key, T2 value){
this.keyValue.put(key, value);
this.valueKey.put(value, key);
}
public T2 getValue(T1 key){
return this.keyValue.get(key);
}
public T1 getKey(T2 value){
return this.valueKey.get(value);
}
}
In java8
map.entrySet().stream().filter(entry -> entry.getValue().equals(value))
.forEach(entry -> System.out.println(entry.getKey()));
Use a thin wrapper: HMap
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
public class HMap<K, V> {
private final Map<K, Map<K, V>> map;
public HMap() {
map = new HashMap<K, Map<K, V>>();
}
public HMap(final int initialCapacity) {
map = new HashMap<K, Map<K, V>>(initialCapacity);
}
public boolean containsKey(final Object key) {
return map.containsKey(key);
}
public V get(final Object key) {
final Map<K, V> entry = map.get(key);
if (entry != null)
return entry.values().iterator().next();
return null;
}
public K getKey(final Object key) {
final Map<K, V> entry = map.get(key);
if (entry != null)
return entry.keySet().iterator().next();
return null;
}
public V put(final K key, final V value) {
final Map<K, V> entry = map
.put(key, Collections.singletonMap(key, value));
if (entry != null)
return entry.values().iterator().next();
return null;
}
}
public static String getKey(Map<String, Integer> mapref, String value) {
String key = "";
for (Map.Entry<String, Integer> map : mapref.entrySet()) {
if (map.getValue().toString().equals(value)) {
key = map.getKey();
}
}
return key;
}
Simplest utility method to fetch a key of a given value from a Map:
public static void fetchValue(Map<String, Integer> map, Integer i)
{
Stream stream = map.entrySet().stream().filter(val-> val.getValue().equals(i)).map(Map.Entry::getKey);
stream.forEach(System.out::println);
}
detailed explaination:
Method fetchValue accepts the map, which has String as key and Integer as value.
Then we use entryset().stream() to convert result into a stream.
Next we use filter (intermediate operation) which gives us a value that is equal to the second argument.
Finally, we use forEach(final operation) to print our end result.
Found too many answers. Some were really great. But I was particularly looking for a way, so that I can get the value using loops.
So here is finally what I did:
For a HashMap 1-to-1 relation:
Map<String, String> map = new HashMap<String, String>();
map.put("abc", "123");
map.put("xyz", "456");
for(Entry<String, String> entry : map.entrySet()) {
if(entry.getValue().equalsIgnoreCase("456")) {
System.out.println(entry.getKey());
}
}
Output: "xyz"
For a HashMap 1-to-many relation:
Map<String, ArrayList<String>> service = new HashMap<String, ArrayList<String>>();
service.put("abc", new ArrayList<String>());
service.get("abc").add("a");
service.get("abc").add("b");
service.get("abc").add("c");
service.put("xyz", new ArrayList<String>());
service.get("xyz").add("x");
service.get("xyz").add("y");
service.get("xyz").add("z");
for(Entry<String, ArrayList<String>> entry : service.entrySet()) {
ArrayList<String> values = entry.getValue();
for(String value : values) {
if(value.equalsIgnoreCase("x")) {
System.out.println(entry.getKey());
}
}
}
Output: xyz
-Thanks
import java.util.HashMap;
import java.util.HashSet;
import java.util.Set;
public class ValueKeysMap<K, V> extends HashMap <K,V>{
HashMap<V, Set<K>> ValueKeysMap = new HashMap<V, Set<K>>();
#Override
public boolean containsValue(Object value) {
return ValueKeysMap.containsKey(value);
}
#Override
public V put(K key, V value) {
if (containsValue(value)) {
Set<K> keys = ValueKeysMap.get(value);
keys.add(key);
} else {
Set<K> keys = new HashSet<K>();
keys.add(key);
ValueKeysMap.put(value, keys);
}
return super.put(key, value);
}
#Override
public V remove(Object key) {
V value = super.remove(key);
Set<K> keys = ValueKeysMap.get(value);
keys.remove(key);
if(keys.size() == 0) {
ValueKeysMap.remove(value);
}
return value;
}
public Set<K> getKeys4ThisValue(V value){
Set<K> keys = ValueKeysMap.get(value);
return keys;
}
public boolean valueContainsThisKey(K key, V value){
if (containsValue(value)) {
Set<K> keys = ValueKeysMap.get(value);
return keys.contains(key);
}
return false;
}
/*
* Take care of argument constructor and other api's like putAll
*/
}
/**
* This method gets the Key for the given Value
* #param paramName
* #return
*/
private String getKeyForValueFromMap(String paramName) {
String keyForValue = null;
if(paramName!=null)) {
Set<Entry<String,String>> entrySet = myMap().entrySet();
if(entrySet!=null && entrySet.size>0) {
for(Entry<String,String> entry : entrySet) {
if(entry!=null && paramName.equalsIgnoreCase(entry.getValue())) {
keyForValue = entry.getKey();
}
}
}
}
return keyForValue;
}
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
public class M{
public static void main(String[] args) {
HashMap<String, List<String>> resultHashMap = new HashMap<String, List<String>>();
Set<String> newKeyList = resultHashMap.keySet();
for (Iterator<String> iterator = originalHashMap.keySet().iterator(); iterator.hasNext();) {
String hashKey = (String) iterator.next();
if (!newKeyList.contains(originalHashMap.get(hashKey))) {
List<String> loArrayList = new ArrayList<String>();
loArrayList.add(hashKey);
resultHashMap.put(originalHashMap.get(hashKey), loArrayList);
} else {
List<String> loArrayList = resultHashMap.get(originalHashMap
.get(hashKey));
loArrayList.add(hashKey);
resultHashMap.put(originalHashMap.get(hashKey), loArrayList);
}
}
System.out.println("Original HashMap : " + originalHashMap);
System.out.println("Result HashMap : " + resultHashMap);
}
}
My 2 cents.
You can get the keys in an array and then loop through the array. This will affect performance of this code block if the map is pretty big , where in you are getting the keys in an array first which might consume some time and then you are looping. Otherwise for smaller maps it should be ok.
String[] keys = yourMap.keySet().toArray(new String[0]);
for(int i = 0 ; i < keys.length ; i++){
//This is your key
String key = keys[i];
//This is your value
yourMap.get(key)
}
While this does not directly answer the question, it is related.
This way you don't need to keep creating/iterating. Just create a reverse map once and get what you need.
/**
* Both key and value types must define equals() and hashCode() for this to work.
* This takes into account that all keys are unique but all values may not be.
*
* #param map
* #param <K>
* #param <V>
* #return
*/
public static <K, V> Map<V, List<K>> reverseMap(Map<K,V> map) {
if(map == null) return null;
Map<V, List<K>> reverseMap = new ArrayMap<>();
for(Map.Entry<K,V> entry : map.entrySet()) {
appendValueToMapList(reverseMap, entry.getValue(), entry.getKey());
}
return reverseMap;
}
/**
* Takes into account that the list may already have values.
*
* #param map
* #param key
* #param value
* #param <K>
* #param <V>
* #return
*/
public static <K, V> Map<K, List<V>> appendValueToMapList(Map<K, List<V>> map, K key, V value) {
if(map == null || key == null || value == null) return map;
List<V> list = map.get(key);
if(list == null) {
List<V> newList = new ArrayList<>();
newList.add(value);
map.put(key, newList);
}
else {
list.add(value);
}
return map;
}

remove similar (redundant) strings from Arraylist

I'm trying to remove similar strings from an ArrayList but I'm getting this error:
CurrentModificationException
and here is my method where I pass my original arrayList (old) and get a new list without redundant strings.
ArrayList<String> removeRed(ArrayList<String> old) throws IOException
{
ArrayList<String> newList = new ArrayList<String>();
for (int i=0; i< old.size(); i++)
{
if(newList.size() < 1)
{
newList.add(old.get(0));
} else{
for(Iterator<String> iterator = newList.iterator(); iterator.hasNext();) {
while(iterator.hasNext())
{
if(!ChopMD((String) iterator.next()).equals(ChopMD(old.get(i))))
{
newList.add(old.get(i));
Log.e("new algo", "" + old.get(i) );
}
}
}
}
}}
Note that my ChopMD() returns a particular string and it works fine.
It works fine for the first few strings, this it throws that exception. Any suggestion to resolve this issue would be appreciated it. Thanks.
If you have no problems with using the standard library (always preferable, why reinvent the wheel) try
List<String> uniques = new ArrayList<String>(new HashSet<String>(oldList));
The HashSet will only contain unique strings and the ArrayList constructor takes any Collection (including a HashSet) to build a list from.
Judging from your comments it seems like you are trying to implement an Associative Array with unique keys using an ArrayList. The better approach is to use a Map implementation like HashMap to pair IDs with their associated Strings.
Map<Integer, String> map = new HashMap<>();
map.put(1, "This string corresponds to ID=1");
map.put(3, "Donald Ducks Nephews");
map.put(7, "Is a Prime");
Then to get a value associated with an ID:
int key = someObject.getID();
String value = map.get(key);
All the Map implementations use unique keys so there is no need for you to check for redundant IDs, if you try to add a new (key,value) pair the value associated with the ID will be replaced if the map contains the key.
map.put(1, "New String");
String s = map.get(1); //s will no longer be "This string corresponds to ID=1"
If you don't want this behavior you have the choice of either subclassing one of the Map implementations to ignore .put(key, value) if the map contains key,value or delegating .put(key,value) to some other class.
Subclassing:
public class UniqueValueHashMap<K,V> extends HashMap<K, V>{
#Override
public V put(K key, V value) {
if (containsKey(key))
return null;
return super.put(key, value);
}
Delegating
public class SomeClass {
private Map<Integer, String> map = new HashMap<>();
// ...stuff this class does
public String put(int key, String value) {
if (map.containsKey(key))
return null;
return map.put(key, value);
}
// ...more stuff this class does
}
Delegation is the better approach, notice how you can change the map implementation (using maybe a TreeMap instead of HashMap) without introducing a new class where you override the .put(key,value) of TreeMap.
You can iterate much easier by this
for (String oldString : old){
for (String newString : newList){
}
}
Also you can use Set to have unique strings
Set<String> newList = new HashSet<String>();
Your error is because you are changing the list WHILE it is still iterated.

Java HashMap: How to get a key and value by index?

I am trying to use a HashMap to map a unique string to a string ArrayList like this:
HashMap<String, ArrayList<String>>
Basically, I want to be able to access the keys by number, not by using the key's name. And I want to be able to access said key's value, to iterate over it. I'm imagining something like this:
for(all keys in my hashmap) {
for(int i=0; i < myhashmap.currentKey.getValue.size(); i++) {
// do things with the hashmaps elements
}
}
Is there an easy way to do this?
Here is the general solution if you really only want the first key's value
Object firstKey = myHashMap.keySet().toArray()[0];
Object valueForFirstKey = myHashMap.get(firstKey);
You can iterate over keys by calling map.keySet(), or iterate over the entries by calling map.entrySet(). Iterating over entries will probably be faster.
for (Map.Entry<String, List<String>> entry : map.entrySet()) {
List<String> list = entry.getValue();
// Do things with the list
}
If you want to ensure that you iterate over the keys in the same order you inserted them then use a LinkedHashMap.
By the way, I'd recommend changing the declared type of the map to <String, List<String>>. Always best to declare types in terms of the interface rather than the implementation.
HashMaps are not ordered, unless you use a LinkedHashMap or SortedMap. In this case, you may want a LinkedHashMap. This will iterate in order of insertion (or in order of last access if you prefer). In this case, it would be
int index = 0;
for ( Map.Entry<String,ArrayList<String>> e : myHashMap.iterator().entrySet() ) {
String key = e.getKey();
ArrayList<String> val = e.getValue();
index++;
}
There is no direct get(index) in a map because it is an unordered list of key/value pairs. LinkedHashMap is a special case that keeps the order.
Kotlin HashMap Answer
You can get key by index. Then get value by key.
val item = HashMap<String, String>() // Dummy HashMap.
val keyByIndex = item.keys.elementAt(0) // Get key by index. I selected "0".
val valueOfElement = item.getValue(keyByIndex) // Get value.
You can do:
for(String key: hashMap.keySet()){
for(String value: hashMap.get(key)) {
// use the value here
}
}
This will iterate over every key, and then every value of the list associated with each key.
A solution is already selected. However, I post this solution for those who want to use an alternative approach:
// use LinkedHashMap if you want to read values from the hashmap in the same order as you put them into it
private ArrayList<String> getMapValueAt(LinkedHashMap<String, ArrayList<String>> hashMap, int index)
{
Map.Entry<String, ArrayList<String>> entry = (Map.Entry<String, ArrayList<String>>) hashMap.entrySet().toArray()[index];
return entry.getValue();
}
for (Object key : data.keySet()) {
String lKey = (String) key;
List<String> list = data.get(key);
}
I came across the same problem, read a couple of answers from different related questions and came up with my own class.
public class IndexableMap<K, V> extends HashMap<K, V> {
private LinkedList<K> keyList = new LinkedList<>();
#Override
public V put(K key, V value) {
if (!keyList.contains(key))
keyList.add(key);
return super.put(key, value);
}
#Override
public void putAll(Map<? extends K, ? extends V> m) {
for (Entry<? extends K, ? extends V> entry : m.entrySet()) {
put(entry.getKey(), entry.getValue());
}
}
#Override
public void clear() {
keyList.clear();
super.clear();
}
public List<K> getKeys() {
return keyList;
}
public int getKeyIndex(K key) {
return keyList.indexOf(key);
}
public K getKeyAt(int index) {
if (keyList.size() > index)
return keyList.get(index);
return null;
}
public V getValueAt(int index) {
K key = getKeyAt(index);
if (key != null)
return get(key);
return null;
}
}
Example (types are differing from OPs question just for clarity):
Map<String, Double> myMap = new IndexableMap<>();
List<String> keys = myMap.getKeys();
int keyIndex = myMap.getKeyIndex("keyString");
String key = myMap.getKeyAt(2);
Double value myMap.getValueAt(2);
Keep in mind that it does not override any of the complex methods, so you will need to do this on your own if you want to reliably access one of these.
Edit: I made a change to the putAll() method, because the old one had a rare chance to cause HashMap and LinkedList being in different states.
Try this:
myhashmap.entrySet()
.forEach{
println(it.getKey())
println(it.getValue())
}
or if you want by index
myhashmap.entrySet()[0].getKey()
myhashmap.entrySet()[0].getValue()
myhashmap.entrySet()[1].getKey()
myhashmap.entrySet()[1].getValue()
HashMaps don't keep your key/value pairs in a specific order. They are ordered based on the hash that each key's returns from its Object.hashCode() method. You can however iterate over the set of key/value pairs using an iterator with:
for (String key : hashmap.keySet())
{
for (list : hashmap.get(key))
{
//list.toString()
}
}
If you don't care about the actual key, a concise way to iterate over all the Map's values would be to use its values() method
Map<String, List<String>> myMap;
for ( List<String> stringList : myMap.values() ) {
for ( String myString : stringList ) {
// process the string here
}
}
The values() method is part of the Map interface and returns a Collection view of the values in the map.
You can use Kotlin extension function
fun LinkedHashMap<String, String>.getKeyByPosition(position: Int) =
this.keys.toTypedArray()[position]
fun LinkedHashMap<String, String>.getValueByPosition(position: Int) =
this.values.toTypedArray()[position]
You'll need to create multiple HashMaps like this for example
Map<String, String> fruitDetails = new HashMap();
fruitDetails.put("Mango", "Mango is a delicious fruit!");
fruitDetails.put("Guava" "Guava is a delicious fruit!");
fruitDetails.put("Pineapple", "Pineapple is a delicious fruit!");
Map<String, String> fruitDetails2 = new HashMap();
fruitDetails2.put("Orange", "Orange is a delicious fruit!");
fruitDetails2.put("Banana" "Banana is a delicious fruit!");
fruitDetails2.put("Apple", "Apple is a delicious fruit!");
// STEP 2: Create a numeric key based HashMap containing fruitDetails so we can access them by index
Map<Integer, Map<String, String>> hashMap = new HashMap();
hashMap.put(0, fruitDetails);
hashMap.put(1, fruitDetails2);
// Now we can successfully access the fruitDetails by index like this
String fruit1 = hashMap.get(0).get("Guava");
String fruit2 = hashMap.get(1).get("Apple");
System.out.println(fruit1); // outputs: Guava is a delicious fruit!
System.out.println(fruit2); // outputs: Apple is a delicious fruit!

Map implementation with duplicate keys

I want to have a map with duplicate keys.
I know there are many map implementations (Eclipse shows me about 50), so I bet there must be one that allows this. I know it's easy to write your own map that does this, but I would rather use some existing solution.
Maybe something in commons-collections or google-collections?
You are searching for a multimap, and indeed both commons-collections and Guava have several implementations for that. Multimaps allow for multiple keys by maintaining a collection of values per key, i.e. you can put a single object into the map, but you retrieve a collection.
If you can use Java 5, I would prefer Guava's Multimap as it is generics-aware.
We don't need to depend on the Google Collections external library. You can simply implement the following Map:
Map<String, ArrayList<String>> hashMap = new HashMap<String, ArrayList>();
public static void main(String... arg) {
// Add data with duplicate keys
addValues("A", "a1");
addValues("A", "a2");
addValues("B", "b");
// View data.
Iterator it = hashMap.keySet().iterator();
ArrayList tempList = null;
while (it.hasNext()) {
String key = it.next().toString();
tempList = hashMap.get(key);
if (tempList != null) {
for (String value: tempList) {
System.out.println("Key : "+key+ " , Value : "+value);
}
}
}
}
private void addValues(String key, String value) {
ArrayList tempList = null;
if (hashMap.containsKey(key)) {
tempList = hashMap.get(key);
if(tempList == null)
tempList = new ArrayList();
tempList.add(value);
} else {
tempList = new ArrayList();
tempList.add(value);
}
hashMap.put(key,tempList);
}
Please make sure to fine tune the code.
Multimap<Integer, String> multimap = ArrayListMultimap.create();
multimap.put(1, "A");
multimap.put(1, "B");
multimap.put(1, "C");
multimap.put(1, "A");
multimap.put(2, "A");
multimap.put(2, "B");
multimap.put(2, "C");
multimap.put(3, "A");
System.out.println(multimap.get(1));
System.out.println(multimap.get(2));
System.out.println(multimap.get(3));
Output is:
[A,B,C,A]
[A,B,C]
[A]
Note: we need to import library files.
http://www.java2s.com/Code/Jar/g/Downloadgooglecollectionsjar.htm
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Multimap;
or https://commons.apache.org/proper/commons-collections/download_collections.cgi
import org.apache.commons.collections.MultiMap;
import org.apache.commons.collections.map.MultiValueMap;
You could simply pass an array of values for the value in a regular HashMap, thus simulating duplicate keys, and it would be up to you to decide what data to use.
You may also just use a MultiMap, although I do not like the idea of duplicate keys myself.
If you want iterate about a list of key-value-pairs (as you wrote in the comment), then a List or an array should be better. First combine your keys and values:
public class Pair
{
public Class1 key;
public Class2 value;
public Pair(Class1 key, Class2 value)
{
this.key = key;
this.value = value;
}
}
Replace Class1 and Class2 with the types you want to use for keys and values.
Now you can put them into an array or a list and iterate over them:
Pair[] pairs = new Pair[10];
...
for (Pair pair : pairs)
{
...
}
This problem can be solved with a list of map entry List<Map.Entry<K,V>>. We don't need to use neither external libraries nor new implementation of Map. A map entry can be created like this:
Map.Entry<String, Integer> entry = new AbstractMap.SimpleEntry<String, Integer>("key", 1);
[June, 2021]
org.springframework.util.MultiValueMap
commons.apache.org - org.apache.commons.collections4
Learn from my mistakes...please don't implement this on your own.
Guava multimap is the way to go.
A common enhancement required in multimaps is to disallow duplicate keys-value pairs.
Implementing/changing this in a your implementation can be annoying.
In Guava its as simple as:
HashMultimap<String, Integer> no_dupe_key_plus_val = HashMultimap.create();
ArrayListMultimap<String, Integer> allow_dupe_key_plus_val = ArrayListMultimap.create();
No fancy libs required.
Maps are defined by a unique key, so dont bend them, use a list. Streams are mighty.
import java.util.AbstractMap.SimpleImmutableEntry;
List<SimpleImmutableEntry<String, String>> nameToLocationMap = Arrays.asList(
new SimpleImmutableEntry<>("A", "A1"),
new SimpleImmutableEntry<>("A", "A2"),
new SimpleImmutableEntry<>("B", "B1"),
new SimpleImmutableEntry<>("B", "B1"),
);
And thats it.
Usage examples:
List<String> allBsLocations = nameToLocationMap.stream()
.filter(x -> x.getKey().equals("B"))
.map(x -> x.getValue())
.collect(Collectors.toList());
nameToLocationMap.stream().forEach(x ->
do stuff with: x.getKey()...x.getValue()...
You can use a TreeMap with a custom Comparator in order to treat each key as unequal to the others. It would also preserve the insertion order in your map, just like a LinkedHashMap. So, the net result would be like a LinkedHashMap which allows duplicate keys!
This is a very simple implementation without the need of any third party dependencies or complications of MultiMaps.
import java.util.Map;
import java.util.TreeMap;
...
...
//Define a TreeMap with a custom Comparator
Map<Integer, String> map = new TreeMap<>((a, b) -> 1); // See notes 1 and 2
//Populate the map
map.put(1, "One");
map.put(3, "Three");
map.put(1, "One One");
map.put(7, "Seven");
map.put(2, "Two");
map.put(1, "One One One");
//Display the map entries:
map.entrySet().forEach(System.out::println);
//See note number 3 for the following:
Map<Integer, String> sortedTreeMap = map.entrySet().stream()
.sorted(Map.Entry.comparingByKey())
.collect(Collectors.toMap(
Map.Entry::getKey, Map.Entry::getValue,
(x, y) -> x, () -> new TreeMap<>((a, b) -> 1)
));
//Display the entries of this sorted TreeMap:
sortedTreeMap.entrySet().forEach(System.out::println);
...
Notes:
You can also use any positive integer in place of 1 in the comparator's definition here.
If you use any negative integer instead, then it will reverse the insertion order in your map.
If you also want to sort this map based on the keys (which is the default behavior of a TreeMap), then you may do this operation on the current map.
I had a slightly different variant of this issue: It was required to associate two different values with same key. Just posting it here in case it helps others, I have introduced a HashMap as the value:
/* #param frameTypeHash: Key -> Integer (frameID), Value -> HashMap (innerMap)
#param innerMap: Key -> String (extIP), Value -> String
If the key exists, retrieve the stored HashMap innerMap
and put the constructed key, value pair
*/
if (frameTypeHash.containsKey(frameID)){
//Key exists, add the key/value to innerHashMap
HashMap innerMap = (HashMap)frameTypeHash.get(frameID);
innerMap.put(extIP, connName+":"+frameType+":"+interfaceName);
} else {
HashMap<String, String> innerMap = new HashMap<String, String>();
innerMap.put(extIP, connName+":"+frameType+":"+interfaceName);
// This means the key doesn't exists, adding it for the first time
frameTypeHash.put(frameID, innerMap );
}
}
In the above code the key frameID is read from a input file's first string in each line, the value for frameTypeHash is constructed by splitting the remaining line and was stored as String object originally, over a period of time the file started having multiple lines (with different values) associated with same frameID key, so frameTypeHash was overwritten with last line as the value. I replaced the String object with another HashMap object as the value field, this helped in maintaining single key to different value mapping.
class DuplicateMap<K, V>
{
enum MapType
{
Hash,LinkedHash
}
int HashCode = 0;
Map<Key<K>,V> map = null;
DuplicateMap()
{
map = new HashMap<Key<K>,V>();
}
DuplicateMap( MapType maptype )
{
if ( maptype == MapType.Hash ) {
map = new HashMap<Key<K>,V>();
}
else if ( maptype == MapType.LinkedHash ) {
map = new LinkedHashMap<Key<K>,V>();
}
else
map = new HashMap<Key<K>,V>();
}
V put( K key, V value )
{
return map.put( new Key<K>( key , HashCode++ ), value );
}
void putAll( Map<K, V> map1 )
{
Map<Key<K>,V> map2 = new LinkedHashMap<Key<K>,V>();
for ( Entry<K, V> entry : map1.entrySet() ) {
map2.put( new Key<K>( entry.getKey() , HashCode++ ), entry.getValue());
}
map.putAll(map2);
}
Set<Entry<K, V>> entrySet()
{
Set<Entry<K, V>> entry = new LinkedHashSet<Map.Entry<K,V>>();
for ( final Entry<Key<K>, V> entry1 : map.entrySet() ) {
entry.add( new Entry<K, V>(){
private K Key = entry1.getKey().Key();
private V Value = entry1.getValue();
#Override
public K getKey() {
return Key;
}
#Override
public V getValue() {
return Value;
}
#Override
public V setValue(V value) {
return null;
}});
}
return entry;
}
#Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("{");
boolean FirstIteration = true;
for ( Entry<K, V> entry : entrySet() ) {
builder.append( ( (FirstIteration)? "" : "," ) + ((entry.getKey()==null) ? null :entry.getKey().toString() ) + "=" + ((entry.getValue()==null) ? null :entry.getValue().toString() ) );
FirstIteration = false;
}
builder.append("}");
return builder.toString();
}
class Key<K1>
{
K1 Key;
int HashCode;
public Key(K1 key, int hashCode) {
super();
Key = key;
HashCode = hashCode;
}
public K1 Key() {
return Key;
}
#Override
public String toString() {
return Key.toString() ;
}
#Override
public int hashCode() {
return HashCode;
}
}
1, Map<String, List<String>> map = new HashMap<>();
this verbose solution has multiple drawbacks and is prone to errors. It
implies that we need to instantiate a Collection for every value, check for
its presence before adding or removing a value, delete it manually when no
values are left, etcetera.
2, org.apache.commons.collections4.MultiMap interface
3, com.google.common.collect.Multimap interface
java-map-duplicate-keys
what about such a MultiMap impl?
public class MultiMap<K, V> extends HashMap<K, Set<V>> {
private static final long serialVersionUID = 1L;
private Map<K, Set<V>> innerMap = new HashMap<>();
public Set<V> put(K key, V value) {
Set<V> valuesOld = this.innerMap.get(key);
HashSet<V> valuesNewTotal = new HashSet<>();
if (valuesOld != null) {
valuesNewTotal.addAll(valuesOld);
}
valuesNewTotal.add(value);
this.innerMap.put(key, valuesNewTotal);
return valuesOld;
}
public void putAll(K key, Set<V> values) {
for (V value : values) {
put(key, value);
}
}
#Override
public Set<V> put(K key, Set<V> value) {
Set<V> valuesOld = this.innerMap.get(key);
putAll(key, value);
return valuesOld;
}
#Override
public void putAll(Map<? extends K, ? extends Set<V>> mapOfValues) {
for (Map.Entry<? extends K, ? extends Set<V>> valueEntry : mapOfValues.entrySet()) {
K key = valueEntry.getKey();
Set<V> value = valueEntry.getValue();
putAll(key, value);
}
}
#Override
public Set<V> putIfAbsent(K key, Set<V> value) {
Set<V> valueOld = this.innerMap.get(key);
if (valueOld == null) {
putAll(key, value);
}
return valueOld;
}
#Override
public Set<V> get(Object key) {
return this.innerMap.get(key);
}
#Override
etc. etc. override all public methods size(), clear() .....
}
Could you also explain the context for which you are trying to implement a map with duplicate keys? I am sure there could be a better solution. Maps are intended to keep unique keys for good reason. Though if you really wanted to do it; you can always extend the class write a simple custom map class which has a collision mitigation function and would enable you to keep multiple entries with same keys.
Note: You must implement collision mitigation function such that, colliding keys are converted to unique set "always". Something simple like, appending key with object hashcode or something?
just to be complete, Apache Commons Collections also has a MultiMap. The downside of course is that Apache Commons does not use Generics.
With a bit hack you can use HashSet with duplicate keys. WARNING: this is heavily HashSet implementation dependant.
class MultiKeyPair {
Object key;
Object value;
public MultiKeyPair(Object key, Object value) {
this.key = key;
this.value = value;
}
#Override
public int hashCode() {
return key.hashCode();
}
}
class MultiKeyList extends MultiKeyPair {
ArrayList<MultiKeyPair> list = new ArrayList<MultiKeyPair>();
public MultiKeyList(Object key) {
super(key, null);
}
#Override
public boolean equals(Object obj) {
list.add((MultiKeyPair) obj);
return false;
}
}
public static void main(String[] args) {
HashSet<MultiKeyPair> set = new HashSet<MultiKeyPair>();
set.add(new MultiKeyPair("A","a1"));
set.add(new MultiKeyPair("A","a2"));
set.add(new MultiKeyPair("B","b1"));
set.add(new MultiKeyPair("A","a3"));
MultiKeyList o = new MultiKeyList("A");
set.contains(o);
for (MultiKeyPair pair : o.list) {
System.out.println(pair.value);
}
}
If there are duplicate keys then a key may correspond to more than one value. The obvious solution is to map the key to a list of these values.
For example in Python:
map = dict()
map["driver"] = list()
map["driver"].append("john")
map["driver"].append("mike")
print map["driver"] # It shows john and mike
print map["driver"][0] # It shows john
print map["driver"][1] # It shows mike
I used this:
java.util.List<java.util.Map.Entry<String,Integer>> pairList= new java.util.ArrayList<>();
Just use simple Set with Pair. This Set will exclude pairs with the same key-value. Also you can iterate it.
val set = hashSetOf<Pair<String, String>>()
set.add(Pair("1", "a"))
set.add(Pair("1", "b"))
set.add(Pair("1", "b")) // Duplicate
set.add(Pair("2", "a"))
set.add(Pair("2", "b"))
set.forEach { pair -> println(pair) }
result: (1, a),(2, b),(1, b),(2, a)

Categories

Resources