Is it possible for a Hashmap to keep its original key/value pair when a duplicate key is entered?
For example, let's say I have something like this:
Map<String, String> map = new HashMap<String, String>();
map.put("username","password1");
map.put("username","password2");
I want the original key/value pair - username, password1 to be kept and not be overrode by username, password2.
Is this possible? If not, how can I eliminate duplicate entries from being put into the map?
As mentioned, you can use putIfAbsent if you use Java 8.
If you are on an older Java version you can use a ConcurrentHashMap instead, which has a putIfAbsent method.
Of course, you get the additional overhead of thread safety, but if you are not writing an extremely performance sensitive application it should not be a concern.
If not on Java 8, you have some options.
The most straightforward is the verbose code everywhere
Object existingValue = map.get(key);
if(existingValue == null){
map.put(key,newValue);
}
You could have a utility method to do this for you
public <T,V> void addToMapIfAbsent(Map<T,V> map, T key, V value){
V oldValue = map.get(key);
if(oldValue == null){
map.put(key,value);
}
}
Or extend a flavor of Map and add it there.
public class MyMap<T,V> extends HashMap<T,V>{
public void putIfNotExist(T key, V value){
V oldValue = get(key);
if(oldValue == null){
put(key,value);
}
}
}
Which allows you to create a Map thusly
Map<String,String> map = new MyMap<>();
EDIT: Although, to get to the MyMap method, of course, you'll need to have the map variable declared as that type. So anywhere you need that, you'll have to take an instance of MyMap instead of Map.
https://docs.oracle.com/javase/8/docs/api/java/util/HashMap.html#putIfAbsent-K-V-
If you are using Java 8, you can use putIfAbsent.
Related
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.
I need to create a map, with 3 columns: 2 keys, and 1 value. So each value will contain 2 keys of different classtypes, and can be fetched by using either one. But my problem is that HashMap/Map supports only 1 key, and 1 value. Is there a way to create something like Map<Key1, Key2, Value> instead of Map<Key, Value>? so Value can be fetched by either using its Key1 or Key2.
I apologize if it is a duplicate or a bad question, but I couldn't find a similar one on Stack Overflow.
P.S: I don't want to create 2 maps: Map<Key1, Value> and Map<Key2, Value> nor creating nested maps I am looking for a multikey table, just one like above.
You're probably going to have to write a custom implementation of a map-like class to implement this. I agree with #William Price above, the easiest implementation will be to simply encapsulate two Map instances. Be careful using the Map interface, as they rely on equals() and hashCode() for key identity, which you intend to break in your contract.
Write class with your requirements by yourself:
import java.util.HashMap;
import java.util.Map;
public class MMap<Key, OtherKey, Value> {
private final Map<Key, Value> map = new HashMap<>();
private final Map<OtherKey, Value> otherMap = new HashMap<>();
public void put(Key key, OtherKey otherKey, Value value) {
if (key != null) { // you can change this, if you want accept null.
map.put(key, value);
}
if (otherKey != null) {
otherMap.put(otherKey, value);
}
}
public Value get(Key key, OtherKey otherKey) {
if (map.containsKey(key) && otherMap.containsKey(otherKey)) {
if (map.get(key).equals(otherMap.get(otherKey))) {
return map.get(key);
} else {
throw new AssertionError("Collision. Implement your logic.");
}
} else if (map.containsKey(key)) {
return map.get(key);
} else if (otherMap.containsKey(otherKey)) {
return otherMap.get(otherKey);
} else {
return null; // or some optional.
}
}
public Value getByKey(Key key) {
return get(key, null);
}
public Value getByOtherKey(OtherKey otherKey) {
return get(null, otherKey);
}
}
Just store the value twice:
Map<Object, Value> map = new HashMap<>();
map.put(key1, someValue);
map.put(key2, someValue);
The thing is, it doesn't really matter what type the key is, so use a generic bound that allows both key types - Object is fine.
Note that the parameter type of Map#get() method is just Object anyway, so from a look-up perspective there's no value in having separate maps (the type of the key is only relevant for put()).
Have you looked into Apache Commons Collection multi map interface?
https://commons.apache.org/proper/commons-collections/javadocs/api-3.2.1/org/apache/commons/collections/MultiMap.html
Take a look at guava's Table collection that can be used in your context
http://docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/collect/Table.html
Table<String,String,String> ==> Map<String,Map<String,String>>
How can I pass in a new HashMap in the most canonical (simplest, shortest hand) form?
// 1. ? (of course this doesn't work)
passMyHashMap(new HashMap<String, String>().put("key", "val"));
// 2. ? (of course this doesn't work)
passMyHashMap(new HashMap<String, String>(){"key", "val"});
void passMyHashMap(HashMap<?, ?> hm) {
// do stuff witih my hashMap
}
Create it, initialize it, then pass it:
Map<String,String> myMap = new HashMap<String,String>();
myMap.put("key", "val");
passMyHashMap(myMap);
You could use the "double curly" style that David Wallace mentions in a comment, I suppose:
passMyHashMap(new HashMap<String,String>(){{
put("x", "y");
put("a", "b");
}});
This essentially derives a new class from HashMap and sets up values in the initializer block. I don't particularly care for it (hence originally not mentioning it), but it doesn't really cause any problems per se, it's more of a style preference (it does spit out an extra .class file, although in most cases that's not a big deal). You could compress it all to one line if you'd like, but readability will suffer.
You can't call put and pass the HashMap into the method at the same time, because the put method doesn't return the HashMap. It returns the old value from the old mapping, if it existed.
You must create the map, populate it separately, then pass it in. It's more readable that way anyway.
HashMap<String, String> map = new HashMap<>();
map.put("key", "val");
passMyHashMap(map);
HashMap< K,V>.put
public **V** put(K key,V value)
Associates the specified value with the specified key in this map. If
the map previously contained a mapping for the key, the old value is
replaced.
Returns the previous value associated with key, or null if there was
no mapping for key. (A null return can also indicate that the map
previously associated null with key.)
As you can see, it does not return the type HashMap<?, ?>
You can't do that. What you can do is create a factory that allow you to do so.
public class MapFactory{
public static Map<String, String> put(final Map<String, String> map, final String key, final String valeu){
map.put(key, value);
return map;
}
}
passMyHashMap(MapFactory.put(new HashMap<String, String>(),"key", "value"));
Although I can't image a approach that would need such implementation, also I kinda don't like it. I would recommend you to create your map, pass the values and just then send to your method.
Map<String, String> map = new HashMap<String, String>();
map.put("key","value");
passMyHashMap(map);
I am looking for a container that would basically work like HashMap where I could put and get any of the entries in O(1) time. I also want to be able to iterate through but I want the order to be sorted by values. So neither TreeMap nor LinkedHashMap would work for me. I found the example below:
SortedSet<Map.Entry<String, Double>> sortedSet = new TreeSet<Map.Entry<String, Double>>(
new Comparator<Map.Entry<String, Double>>() {
#Override
public int compare(Map.Entry<String, Double> e1,
Map.Entry<String, Double> e2) {
return e1.getValue().compareTo(e2.getValue());
}});
The problem is SortedSet doesn't have any get method to get entries.
I will be using this collection in a place where I will be adding entries but in case of already existing entry, the value(double) will be updated and then sorted again by using the comparator(which compares values as mentioned above). What can I use for my needs?
There is no such data structure in the Java class libraries.
But you could create one that is a wrapper for a private HashMap and a private TreeMap with the same set of key/value pairs.
This gives a data structure that has get complexity of O(1) like a regular HashMap (but not put or other update operations), and a key Set and entry Set which can be iterated in key order ... as requested in the original version of this Question.
Here is a start for you:
public class HybridMap<K,V> implements Map<K,V> {
private HashMap<K,V> hashmap = ...
private TreeMap<K,V> treemap = ...
#Override V get(K key) {
return hashmap.get(key);
}
#Override void (K key, V value) {
hashmap.put(key, value);
treemap.put(key, value);
}
// etcetera
}
Obviously, I've only implemented some of the (easy) methods.
If you want to be able to iterate the entries in value order (rather than key order), once again there is no such data structure in the Java class libraries.
In this case, wrapping a HashMap and TreeMap is going to be very complicated if you need the resulting Map to conform fully to the API contract.
So I suggest that you just use a HashMap and TreeSet of the key/value pairs ... and manually keep them in step.
I have a method that goes through the possible states in a board and stores them in a HashMap
void up(String str){
int a = str.indexOf("0");
if(a>2){
String s = str.substring(0,a-3)+"0"+str.substring(a-2,a)+str.charAt(a-3)+str.substring(a+1);
add(s,map.get(str)+1);
if(s.equals("123456780")) {
System.out.println("The solution is on the level "+map.get(s)+" of the tree");
//If I get here, I need to know the keys on the map
// How can I store them and Iterate through them using
// map.keySet()?
}
}
}
I'm interested in the group of keys. What should I do to print them all?
HashSet t = map.keySet() is being rejected by the compiler as well as
LinkedHashSet t = map.keySet()
Use:
Set<MyGenericType> keySet = map.keySet();
Always try to specify the Interface type for collections returned by these methods. This way regardless of the actual implementation class of the Set returned by these methods (in your case map.keySet()) you would be ok. This way if the next release the jdk guys use a different implementation for the returned Set your code will still work.
map.keySet() returns a View on the Keys of the map. Making changes to this view results in changing the underlying map though those changes are limited. See the javadoc for Map:
http://java.sun.com/j2se/1.5.0/docs/api/java/util/Map.html#keySet%28%29
Map<String, String> someStrings = new HashMap<String, String>();
for(Map.Entry<String, String> entry : someStrings.entrySet()) {
String key = entry.getKey();
String value = entry.getValue();
}
This is how I like to iterate through Maps. If you specifically want just the keySet(), that answer is elsewhere on this page.
for ( String key : map.keySet() ) {
System.out.println( key );
}
Set t = map.ketSet()
The API does not specify what type of Set is returned.
You should try to declare variables as the interface rather than a particular implementation.
Just
Set t = map.keySet();
Unless you're using an older JDK, I think its a little cleaner to use generics when using the Collections classes.
So thats
Set<MyType> s = map.keySet();
And then if you just iterate through them, then you can use any kind of loop you'd like. But if you're going to be modifying the map based on this keySet, you you have to use the keySet's iterator.
All that's guaranteed from keySet() is something that implements the interface Set. And that could possibly be some undocumented class like SecretHashSetKeys$foo, so just program to the interface Set.
I ran into this trying to get a view on a TreeSet, the return type ended up being TreeSet$3 on close examination.
Map<String, Object> map = new HashMap<>();
map.put("name","jaemin");
map.put("gender", "male");
map.put("age", 30);
Set<String> set = map.keySet();
System.out.println("this is map : " + map);
System.out.println("this is set : " + set);
It puts the key values in the map into the set.
From Javadocs HashMap has several methods that can be used to manipulate and extract data from a hasmap.
public Set<K> keySet()
Returns a Set view of the keys contained in this map. The set is backed by the map, so changes to the map are reflected in the set, and vice-versa. If the map is modified while an iteration over the set is in progress (except through the iterator's own remove operation), the results of the iteration are undefined. The set supports element removal, which removes the corresponding mapping from the map, via the Iterator.remove, Set.remove, removeAll, retainAll, and clear operations. It does not support the add or addAll operations.
Specified by:
keySet in interface Map
Overrides:
keySet in class AbstractMap
Returns:
a set view of the keys contained in this map
so if you have a map myMap of any datatype , such that the map defined as map<T> , if you iterate it as follows:
for (T key : myMap.keySet() ) {
System.out.println(key); // which represent the value of datatype T
}
e.g if the map was defined as Map<Integer,Boolean>
Then for the above example we will have:
for (Integer key : myMap.keySet()){
System.out.println(key) // the key printed out will be of type Integer
}