When I put a (KEY, VALUE) into a map such as Map<String, List<String>>, and I want to check if the KEY is existed first to decide if I have to make a new List, usually My Java Code looks like this:
Map<String, List<String>> example = new HashMap<>();
public void put(String k, String v){
if(example.containsKey(k)){
example.get(k).add(v);
return;
}
List<String> vs = new ArrayList<>();
vs.add(v);
example.put(k,vs);
}
It doesn't looks very nice. Is there any way to make it more simple and more beautiful?
If you have Java 8 you can write this as one line:
example.computeIfAbsent(k, key -> new ArrayList<>()).add(v);
This uses a lambda, so the new ArrayList is only created if required.
(k and key need to have different names, as they are different variables)
Map<String, List<String>> example = new HashMap<>();
public void put(String k, String v){
if (!example.containsKey(k)){
example.put(k, new ArrayList<>();
}
example.get(k).add(v);
}
Arguably, this is slightly wasteful - requiring you to get the list you just put - but to my eye it is much cleaner and more expressive.
If you can't use other libraries, or java 8, you could wrap the whole map and construct in a class of you own.
With your own class you:
Confine the messiness to one place.
Hide the face you are using a Map behind the scenes.
Have a place to move any additional logic to.
Related
Previously I had the TreeMap that is sorted via:
Map<String, Set<Stuff>> map = new TreeMap<>(String::compareToIgnoreCase);
I'm thinking of adding a special case. If the string is "Front", I want to ignore this ordering and just have it in the beginning of the map.
Map<String, Set<Stuff>> map = new TreeMap<>((s1, s2) -> {
//do a check to see if the string is "Front", otherwise use the above
});
This feels a bit convoluted to me... Is there a simpler way to do this?
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.
For my Android app I've the need of defining some keys in a single constant, and I think the best way to do it is using a map. But not sure whether that's really the way to go, and how to do it correctly. As I'm targeting Android, a Bundle may also be an option.
I have a list of keys like:
"h" = "http"
"f" = "ftp"
Basically the program is to read a QR code (to keep that code from growing too big I'm using super-short keys), gets those keys, and has to translate them to something useful, in my case a protocol.
I'm trying to define a constant called KEY_PROTOCOLS, I think this should be a Map, so later I can call something like KEY_PROTOCOLS.get("f") to get the protocol that belongs to key "f".
Other classes should also be able to import this constant, and use it. So this map has to be populated in the class right away.
How can I do this?
If the constant is shared by several classes, and if you want to make sure this map is not cleared or modified by some code, you'd better make it unmodifiable :
public static final Map<String, String> KEY_PROTOCOLS;
static {
Map<String, String> map = new HashMap<String, String>();
map.put("f", "ftp");
// ...
KEY_PROTOCOLS = Collections.unmodifiableMap(map);
}
Something like this:
private static final Map<String, String> KEY_PROTOCOLS = new HashMap<String, String>();
static{
KEY_PROTOCOLS.put("f", "ftp");
// More
}
Static Initialisers:
http://www.glenmccl.com/tip_003.htm
This would work.
static Map<String, String> map = new HashMap<String, String>();
static {
map.add("ftp", "ftp");
...
}
On android:
#SuppressWarnings("unchecked")
Pair<String,String>[] pre_ips=new Pair[]{new Pair<String,String>("173.194", "0"), new Pair<String,String>("74.125", "96")};
String ip_1_2,ip_3;
for (Pair<String,String> pre_ip:pre_ips)
{ip_1_2=pre_ip.first;
ip_3=pre_ip.second;
}
I already know how to do it the hard way and got it working - iterating over entries and swapping "manually". But i wonder if, like so many tasks, this one can be solved in a more elegant way.
I have read this post, unfortunately it does not feature elegant solutions. I also have no possibility to use any fancy Guava BiMaps or anything outside the jdk (project stack is already defined).
I can assume that my map is bijective, btw :)
Map<String, Integer> map = new HashMap<>();
Map<Integer, String> swapped = map.entrySet().stream().collect(Collectors.toMap(Map.Entry::getValue, Map.Entry::getKey));
If you don't have a choice to use a third party library, I don't consider the following code so ugly (though some scripting languages do have elegant ways of doing it):
//map must be a bijection in order for this to work properly
public static <K,V> HashMap<V,K> reverse(Map<K,V> map) {
HashMap<V,K> rev = new HashMap<V, K>();
for(Map.Entry<K,V> entry : map.entrySet())
rev.put(entry.getValue(), entry.getKey());
return rev;
}
The standard API / Java runtime doesn't offer a bi-directional map, so the only solution is to iterate over all entries and swap them manually.
What you can do is create a wrapper class which contains two maps and which does a dual put() internally so you have fast two views on the data.
[EDIT] Also, thanks to open source, you don't have to include a third party library, you can simply copy the classes you need into your own project.
Maps are not like lists, which can be reversed by swapping head with tail.
Objects in maps have a computed position, and using the value as key and the key as value would requiere to re-compute the storage place, essentialy building another map. There is no elegant way.
There are, however, bidirectional maps. Those may suit your needs. I'd reconsider using third-party libraries.
There are some jobs that can be simplified to a certain point and no more. This may be one of them!
If you want to do the job using Java collections apis only then brute force is the way to go - it will be quick (unless the collection is huge) and it will be an obvious piece of code.
As a hint to answer
https://stackoverflow.com/a/42091477/8594421
This only works, if the map is not a HashMap and does not contain duplicate values.
Map<String,String> newMap = oldMap.entrySet().stream().collect(Collectors.toMap(Map.Entry::getValue, Map.Entry::getKey));
throws an exception
java.lang.IllegalStateException: Duplicate key
if there are values more than once.
The solution:
HashMap<String,String> newMap = new HashMap<>();
for(Map.Entry<String,String> entry : oldMap.entrySet())
newMap.put(entry.getValue(), entry.getKey());
// Add inverse to old one
oldMap.putAll(newMap);
If you had access to apache commons-collections, you could have used MapUtils.invertMap.
Note: The behaviour in case of duplicated values is undefined.
(Replying to this as this is the first google result for "java invert map").
Java stream API provides nice set of APIs that would help you with this.
If the values are unique then the below would work. When I mean values, I mean the V in the Map<K, V>.
Map<String, Integer> map = new HashMap<>();
Map<Integer, String> swapped = map.entrySet()
.stream()
.collect(Collectors.toMap(Map.Entry::getValue, Map.Entry::getKey));
If the values are not unique, then use below:
Map<Integer, List<String>> swapped = map.entrySet()
.stream()
.collect(Collectors.groupingBy(Map.Entry::getValue, Collectors.mapping(Map.Entry::getKey, Collectors.toList())));
Thanks Nikita and FreyaZ. Posting as new answer as there were so many edit queues for Nikita's Answer
This will work for duplicate values in the map also, but not for HashMap as values.
package Sample;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
public class Sample {
public static void main(String[] args) {
Map<String,String> map = new HashMap<String,String>();
Map<String, Set<String> > newmap = new HashMap<String, Set<String> >();
map.put("1", "a");
map.put("2", "a");
map.put("3", "b");
map.put("4", "b");
System.out.println("before Reversing \n"+map.toString());
for (Map.Entry<String, String> entry : map.entrySet())
{
String oldVal = entry.getValue();
String oldKey = entry.getKey();
Set<String> newVal = null;
if (newmap.containsKey(oldVal))
{
newVal = newmap.get(oldVal);
newVal.add(oldKey);
}
else
{
newVal= new HashSet<>();
newVal.add(oldKey);
}
newmap.put(oldVal, newVal);
}
System.out.println("After Reversing \n "+newmap.toString());
}
}
I'm used to working with PHP but lately I've been working with Java and I'm having a headache trying to figure this out. I want to save this representation in Java:
Array (
["col_name_1"] => Array (
1 => ["col_value_1"],
2 => ["col_value_2"],
... ,
n => ["col_value_n"]
),
["col_name_n"] => Array (
1 => ["col_value_1"],
2 => ["col_value_2"],
... ,
n => ["col_value_n"]
)
)
Is there a clean way (i.e. no dirty code) to save this thing in Java? Note; I would like to use Strings as array indexes (in the first dimension) and I don't know the definite size of the arrays..
Try using a Map<String, List<String>>. This will allow you to use Strings as keys / indices into the outer map and get a result being a list of Strings as values. You'll probably want to use a HashMap for the outer map and ArrayList's for the inner lists.
If you want some clean code that is similar to the PHP you gave to initialize it, you can do something like this:
Map<String, List<String>> columns = new HashMap<String, List<String>>() {{
put("col_name_1", Arrays.asList("col_val_1", "col_val_2", "col_val_n"));
put("col_name_2", Arrays.asList("col_val_1", "col_val_2", "col_val_n"));
put("col_name_n", Arrays.asList("col_val_1", "col_val_2", "col_val_n"));
}};
You can use a Map and a List (these both are interfaces implemented in more than one way for you to choose the most adequate in your case).
For more information check the tutorials for Map and List and maybe you should start with the Collections tutorial.
An example:
import java.util.*;
public class Foo {
public static void main(String[] args) {
Map<String, List<String>> m = new HashMap<String, List<String>>();
List<String> l = new LinkedList<String>();
l.add("col_value_1");
l.add("col_value_2");
//and so on
m.put("col_name_1",l); //repeat for the rest of the colnames
//then, to get it you do
List<String> rl = m.get("col_name_1");
}
}
You want a Map, which are keyed by just about anything. HashMaps work in most cases.
Something like this.
List<String> col1Vals = new java.util.ArrayList<String>();
col1Vals.add("col_value_1");
col1Vals.add("col_value_2");
Map<String, List<String>> map = new HashMap<String, List<String>>();
map.put("col_name_1", col1Vals);
If you want something simpler, the commons-lang library has a MultiMap.
Be forewarned that the Vector is legacy code for the Collections framework. It synchronizes access to its elements which slows down performance. Most use cases for using List don't need this kind of thread safety, and even if you did, I would be more inclined to use the CopyOnWriteArrayList.