Map with multi values - java

I basically want 2 values on 1 map if possible or something equivalent. I want to store this info
Map<K,V1,V2> sample = new HasMap<K,V1,V2>
(Key - caller) = 26
(value 1 - callee) = 55
(value 2 - seconds) = 550
sample(26,55,550)
the only other way i see how i can do this is
Map(k, arraylist(v2))
having the position in the arraylist as V1 but this will take forever to search if i would want to find what callers have called a specific callee.
i have also read this HashMap with multiple values under the same key but i do not understand how to do this.

Create a bean for your value like below
class Value {
VariableType val1;
VariableType val2;
...
}
Then we can create a map like below
Map<K,Value> valueSample = new HashMap<K,Value>();
valueSample .put(new K(), new Value());
We need to set the value in Value calss by setter or constructor

One solution is to create a wrapper object (it was given in the discussion link that you have in your question) to hold values v1 and v2:
class ValueWrapper {
V1Type v1;
V2Type v2;
...
}
Now you can map your keys to the instances of this wrapper:
Map<K,ValueWrapper> sample = new HashMap<K,ValueWrapper>();
sample.put(new K(), new ValueWrapper());

If all values are <V1, V2> you can use Entry as value:
Map<K,Map.Entry<V1,V2>> sample = new HasMap<K,Map.Entry<V1,V2>>();
Create Entry<V1,V2> and put it with the relevant key.
Another solution (and even better one) is to create your own value class.

You can do like this.
Map<Integer,List<Integer>> map = new HashMap<Integer,List<Integer>>();
List<Integer> list =new ArrayList<Integer>();
list.add(55);
list.add(550);
//Adding value to the map
map.put(26, list);
//getting value from map
List<Integer> values = map.get(26);

Related

HashMap getting key given that I already have the key

This sounds like a really weird request but I have a HashMap of a custom class. I've overridden the equals and hashCode methods to only focus on certain fields, so that I can pull a key if it equals a new key with the same certain fields. In that case, I want to replace the other fields with some new values. The structure is like so:
public class ExampleClass() {
int field1;
int field2;
<insert constructor here with field1 and field2>
#Override
public boolean equals(Object obj) { // Only return true if field1 is equal
...
return (this.field1 == obj.field1);
}
}
So I use it like this:
HashMap<ExampleClass, int> hmap = new HashMap<>();
while(true) {
...
ExampleClass oldObject = new ExampleClass(1, 2);
ExampleClass newObject = new ExampleClass(1, 5);
hmap.put(oldObject, 10);
if(hmap.contains(newObject)) {
// Get field1 of old object and change it
}
}
This was a bad example but I just want to be able to retrieve the key object of a key-value pair in a HashMap given that I have the key so that I can modify the key. How would I do so?
Edit: My hashcode function.
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result +
((this.srcVertex.getVertexData().getID() == null) ? 0 : this.srcVertex.getVertexData().getID().hashCode());
result = prime * result +
((this.targetVertex.getVertexData().getID() == null) ? 0 : this.targetVertex.getVertexData().getID().hashCode());
return result;
}
As I understand your question, you have a key object in the HashMap, and you want to use an "equal" key object to retrieve the key rather than the value it's associated with. There is no method on a HashMap to do that, and it somewhat violates the idea of two objects being "equal" if you do actually care which of the two equal objects you get.
I think it would make more sense to do this in a different way:
Write a new class ExampleKey with just the fields that you want to use in the equals/hashCode methods for the purposes of the HashMap. This class must override equals and hashCode using those fields, and it should be immutable (the behaviour is undefined if a key's hash can change while it's in the HashMap).
Give ExampleClass a getKey() method which returns an ExampleKey object for the current object. It is probably simpler to use composition here, so that ExampleClass doesn't duplicate those fields.
Now have two HashMaps: a Map<ExampleKey, Integer> for the actual mapping that you want to store, and a separate Map<ExampleKey, ExampleClass> storing the object which would otherwise have been used as the key in the other HashMap.
Example usage:
Map<ExampleKey, Integer> actualMapping = new HashMap<>();
Map<ExampleKey, ExampleClass> objsUsed = new HashMap<>();
while(true) {
// ...
ExampleClass oldObject = new ExampleClass(1, 2);
ExampleClass newObject = new ExampleClass(1, 5);
// always update both maps together, to ensure valid state
actualMapping.put(oldObject.getKey(), 10);
objsUsed.put(oldObject.getKey(), oldObject);
// ...
ExampleClass objUsed = objsUsed.get(newObject.getKey());
if(objUsed != null) {
// objUsed == oldObject here
}
}
If you don't care about the philosophy of what "equal" is supposed to mean, then you can apply this same solution without the ExampleKey class or the getKey method; just use the objects themselves, i.e. objsUsed would be of type Map<ExampleClass, ExampleClass> and it would always map an object to itself. But I think if you do that, readers of your code will be scratching their heads wondering why you are mapping objects to themselves.
Maps in Java should be keyed on values that have equals defined over all their 'essential properties'. I believe most collection libraries work like this, with the only example that springs to mind is that of General Magic's Telescript.
So, have a Map defined on a type of only those properties. The field1 int (Integer) in this case. Put the rest of the information in the map entry value. This may well be a new class.
Map<Integer, ValueClass> map;
where
public final class ValueClass {
private int someValue;
private ExampleClass exmaple;
...
If you are insistent you want to find the key, which I suggest you don't. There's various ways of doing it, something like:
Optional<ExampleClass> found = map.keySet().stream()
.firstThat(k -> k.field1() == target);
found.ifPresent(key -> {
Integer value = hmap.remove(key);
// update key.
hmap.put(key, value);
});
Or the old school version (looks better to me, but not so cool):
for (ExampleClass key : map.keySet()) {
if (key.field1() == target) {
Integer value = hmap.remove(key);
// update key.
hmap.put(key, value);
}
}
Using an Iterator or possibly over an ihe entry set is better in that it avoids the second of three lookups, but I'll leave that as an exercise.
You forgot to define the hashCode() method. Without it, storing an object as a key in a HashMap doesn't work.
UPDATE:
If srcVertex is field1 and targetVertex is field2, then your hashCode() method is incorrect. If equals() compares srcVertex, then hashCode() should use only srcVertex, not targetVertex.
The rule is: if 2 objects are equal, then their hash codes must be equal.

Get total for unique items from Map in java

I currently have a map which stores the following information:
Map<String,String> animals= new HashMap<String,String>();
animals.put("cat","50");
animals.put("bat","38");
animals.put("dog","19");
animals.put("cat","31");
animals.put("cat","34");
animals.put("bat","1");
animals.put("dog","34");
animals.put("cat","55");
I want to create a new map with total for unique items in the above map. So in the above sample, count for cat would be 170, count for bat would be 39 and so on.
I have tried using Set to find unique animal entries in the map, however, I am unable to get the total count for each unique entry
First, don't use String for arithmetic, use int or double (or BigInteger/BigDecimal, but that's probably overkill here). I'd suggest making your map a Map<String, Integer>.
Second, Map.put() will overwrite the previous value if the given key is already present in the map, so as #Guy points out your map actually only contains {cat:55, dog:34, bat:1}. You need to get the previous value somehow in order to preserve it.
The classic way (pre-Java-8) is like so:
public static void putOrUpdate(Map<String, Integer> map, String key, int value) {
Integer previous = map.get(key);
if (previous != null) {
map.put(key, previous + value);
} else {
map.put(key, value);
}
}
Java 8 adds a number of useful methods to Map to make this pattern easier, like Map.merge() which does the put-or-update for you:
map.merge(key, value, (p, v) -> p + v);
You may also find that a multiset is a better data structure to use as it handles incrementing/decrementing for you; Guava provides a nice implementation.
As Guy said. Now you have one bat, one dog and one cat. Another 'put's will override your past values. For definition. Map stores key-value pairs where each key in map is unique. If you have to do it by map you can sum it just in time. For example, if you want to add another value for cat and you want to update it you can do it in this way:
animals.put("cat", animals.get("cat") + yourNewValue);
Your value for cat will be updated. This is for example where our numbers are float/int/long, not string as you have. If you have to do it by strings you can use in this case:
animals.put("cat", Integer.toString(Integer.parseInt(animals.get("cat")) + yourNewValue));
However, it's ugly. I'd recommend create
Map<String, Integer> animals = new HashMap<String, Integer>();

Access to the key-value pair of a Map with one element in Java

A method of mine returns a Map<A,B>. In some clearly identified cases, the map only contains one key-value pair, effectively only being a wrapper for the two objects.
Is there an efficient / elegant / clear way to access both the key and the value? It seems overkill to iterate over the one-element entry set. I'm looking for somehing that would lower the brain power required for people who will maintain this, along the lines of:
(...)
// Only one result.
else {
A leKey = map.getKey(whicheverYouWantThereIsOnlyOne); // Is there something like this?
B leValue = map.get(leKey); // This actually exists. Any Daft Punk reference was non-intentional.
}
Edit: I ended up going with #akoskm solution's below. In the end, the only satisfying way of doing this without iteration was with a TreeMap, and the overhead made that unreasonable.
It turns out there is not always a silver bullet, especially as this would be a very small rabbit to kill with it.
If you need both key/value then try something like this:
Entry<Long, AccessPermission> onlyEntry = map.entrySet().iterator().next();
onlyEntry.getKey();
onlyEntry.getValue();
You can use TreeMap or ConcurrentSkipListMap.
TreeMap<String, String> myMap = new TreeMap<String, String>();
String firstKey = myMap.firstEntry().getKey();
String firstValue = myMap.firstEntry().getValue();
Another way to use this:
String firstKey = myMap.firstKey();
String firstValue = myMap.get(myMap.firstKey());
This can work as an alternate solution.
There is a method called keySet() to get set of keys. read this thread.
else {
A leKey=map.keySet().iterator().next();
B leValue; = map.get(leKey); // This actually exists. Any Daft Punk reference was non-intentional.
}
Using for-each loop and var :
for(var entry : map.entrySet()){
A key = entry.getKey();
B value = entry.getValue();
}

How to split the string on a dot in such a way such that each user has set of attributes?

I am not sure what wrong I am doing here. I have a List of string (userIdList) which looks like below -
[event.1386979194020.24551521.DC1, modela.1386979194020.24551521.DC1]
Above format is like below -
A.B.C.D
Here, C is the user-id and A is the attribute name. So in the above example, for the first string in the above list - 24551521 is the user-id and event is the attribute name. Similarly for the second string in the above list, modela is the attribute name and 24551521 is the user-id.
As you can see that, 24551521 user-id has two attributes which are event and modela.
So I am trying to iterate the userIdList and make a map such that, key in the map has 24551521 as the user-id and value in the map will be a set of string and it should be event and modela,
But somehow, in my below code, I always see - 24551521 as the key of map and modela in the set of string value not both event and modela in the set.
Below is my Java code in which I am trying to iterate userIdList-
Map<String, Set<String>> userAttribute = new LinkedHashMap<String, Set<String>>();
for(String ss : userIdList) {
Set<String> attributeTypes = new LinkedHashSet<String>();
attributeTypes.add(ss.split("\\.")[0]);
// this always override my previous set value
userAttribute.put(ss.split("\\.")[2], attributeTypes);
}
If I print out userAttribute map, I always see 24551521 as the key and modela as the set value which is not what I want. But it should be 24551521 and set of strings - event and modela
Any thoughts what wrong I am doing here?
In each iteration you are defining new Set and then writing it to Map (overwriting old one).
You should check if there is Set for this id and update it
Set<String> attributeTypes;
String id = ss.split("\\.")[2];
if(userAttribute.containsKey(id))
attributeTypes = userAttribute.get(id);
else
attributeTypes = new LinkedHashSet<String>();
attributeTypes.add(ss.split("\\.")[0]);
userAttribute.put(id, attributeTypes);
Only create the Set for the key once, by checking if it's there first and if it's not, create a new one and put it in the map:
Map<String, Set<String>> userAttribute = new LinkedHashMap<String, Set<String>>();
for (String ss : userIdList) {
String id = ss.split("\\.")[2];
Set<String> attributeTypes = userAttribute.get(id);
if (attributeTypes == null) { // null is returned if there's no entry
attributeTypes = new LinkedHashSet<String>();
userAttribute.put(id, attributeTypes);
}
attributeTypes.add(ss.split("\\.")[0]);
}
Notice how there's only a call to map.put() (if it's a new id encountered). Also, there's only one call to map.get() and no calls to map.contains() - this is the most efficient way to do it.

What happens when a duplicate key is put into a HashMap?

If I pass the same key multiple times to HashMap’s put method, what happens to the original value? And what if even the value repeats? I didn’t find any documentation on this.
Case 1: Overwritten values for a key
Map mymap = new HashMap();
mymap.put("1","one");
mymap.put("1","not one");
mymap.put("1","surely not one");
System.out.println(mymap.get("1"));
We get surely not one.
Case 2: Duplicate value
Map mymap = new HashMap();
mymap.put("1","one");
mymap.put("1","not one");
mymap.put("1","surely not one");
// The following line was added:
mymap.put("1","one");
System.out.println(mymap.get("1"));
We get one.
But what happens to the other values? I was teaching basics to a student and I was asked this. Is the Map like a bucket where the last value is referenced (but in memory)?
By definition, the put command replaces the previous value associated with the given key in the map (conceptually like an array indexing operation for primitive types).
The map simply drops its reference to the value. If nothing else holds a reference to the object, that object becomes eligible for garbage collection. Additionally, Java returns any previous value associated with the given key (or null if none present), so you can determine what was there and maintain a reference if necessary.
More information here: HashMap Doc
You may find your answer in the javadoc of Map#put(K, V) (which actually returns something):
public V put(K key,
V value)
Associates the specified value with the specified key in this map
(optional operation). If the map
previously contained a mapping for
this key, the old value is replaced by
the specified value. (A map m is said
to contain a mapping for a key k if
and only if m.containsKey(k) would
return true.)
Parameters:
key - key with which the specified value is to be associated.
value - value to be associated with the specified key.
Returns:
previous value associated with specified key, or null if there was no
mapping for key. (A null return can also indicate that the map previously associated null with the specified key, if the implementation supports null values.)
So if you don't assign the returned value when calling mymap.put("1", "a string"), it just becomes unreferenced and thus eligible for garbage collection.
it's Key/Value feature and you could not to have duplicate key for several values because when you want to get the actual value which one of values is belong to entered keyin your example when you want to get value of "1" which one is it ?!that's reasons to have unique key for every value but you could to have a trick by java standard lib :
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
public class DuplicateMap<K, V> {
private Map<K, ArrayList<V>> m = new HashMap<>();
public void put(K k, V v) {
if (m.containsKey(k)) {
m.get(k).add(v);
} else {
ArrayList<V> arr = new ArrayList<>();
arr.add(v);
m.put(k, arr);
}
}
public ArrayList<V> get(K k) {
return m.get(k);
}
public V get(K k, int index) {
return m.get(k).size()-1 < index ? null : m.get(k).get(index);
}
}
and you could to use it in this way:
public static void main(String[] args) {
DuplicateMap<String,String> dm=new DuplicateMap<>();
dm.put("1", "one");
dm.put("1", "not one");
dm.put("1", "surely not one");
System.out.println(dm.get("1"));
System.out.println(dm.get("1",1));
System.out.println(dm.get("1", 5));
}
and result of prints are :
[one, not one, surely not one]
not one
null
It replaces the existing value in the map for the respective key. And if no key exists with the same name then it creates a key with the value provided.
eg:
Map mymap = new HashMap();
mymap.put("1","one");
mymap.put("1","two");
OUTPUT
key = "1", value = "two"
So, the previous value gets overwritten.
The prior value for the key is dropped and replaced with the new one.
If you'd like to keep all the values a key is given, you might consider implementing something like this:
import org.apache.commons.collections.MultiHashMap;
import java.util.Set;
import java.util.Map;
import java.util.Iterator;
import java.util.List;
public class MultiMapExample {
public static void main(String[] args) {
MultiHashMap mp=new MultiHashMap();
mp.put("a", 10);
mp.put("a", 11);
mp.put("a", 12);
mp.put("b", 13);
mp.put("c", 14);
mp.put("e", 15);
List list = null;
Set set = mp.entrySet();
Iterator i = set.iterator();
while(i.hasNext()) {
Map.Entry me = (Map.Entry)i.next();
list=(List)mp.get(me.getKey());
for(int j=0;j<list.size();j++)
{
System.out.println(me.getKey()+": value :"+list.get(j));
}
}
}
}
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.
To your question whether the map was like a bucket: no.
It's like a list with name=value pairs whereas name doesn't need to be a String (it can, though).
To get an element, you pass your key to the get()-method which gives you the assigned object in return.
And a Hashmap means that if you're trying to retrieve your object using the get-method, it won't compare the real object to the one you provided, because it would need to iterate through its list and compare() the key you provided with the current element.
This would be inefficient. Instead, no matter what your object consists of, it calculates a so called hashcode from both objects and compares those. It's easier to compare two ints instead of two entire (possibly deeply complex) objects. You can imagine the hashcode like a summary having a predefined length (int), therefore it's not unique and has collisions. You find the rules for the hashcode in the documentation to which I've inserted the link.
If you want to know more about this, you might wanna take a look at articles on javapractices.com and technofundo.com
regards
Maps from JDK are not meant for storing data under duplicated keys.
At best new value will override the previous ones.
Worse scenario is exception (e.g when you try to collect it as a stream):
No duplicates:
Stream.of("one").collect(Collectors.toMap(x -> x, x -> x))
Ok. You will get: $2 ==> {one=one}
Duplicated stream:
Stream.of("one", "not one", "surely not one").collect(Collectors.toMap(x -> 1, x -> x))
Exception java.lang.IllegalStateException: Duplicate key 1 (attempted merging values one and not one)
| at Collectors.duplicateKeyException (Collectors.java:133)
| at Collectors.lambda$uniqKeysMapAccumulator$1 (Collectors.java:180)
| at ReduceOps$3ReducingSink.accept (ReduceOps.java:169)
| at Spliterators$ArraySpliterator.forEachRemaining (Spliterators.java:948)
| at AbstractPipeline.copyInto (AbstractPipeline.java:484)
| at AbstractPipeline.wrapAndCopyInto (AbstractPipeline.java:474)
| at ReduceOps$ReduceOp.evaluateSequential (ReduceOps.java:913)
| at AbstractPipeline.evaluate (AbstractPipeline.java:234)
| at ReferencePipeline.collect (ReferencePipeline.java:578)
| at (#4:1)
To deal with duplicated keys - use other package, e.g:
https://google.github.io/guava/releases/19.0/api/docs/com/google/common/collect/Multimap.html
There is a lot of other implementations dealing with duplicated keys.
Those are needed for web (e.g. duplicated cookie keys, Http headers can have same fields, ...)
Good luck! :)
I always used:
HashMap<String, ArrayList<String>> hashy = new HashMap<String, ArrayList<String>>();
if I wanted to apply multiple things to one identifying key.
public void MultiHash(){
HashMap<String, ArrayList<String>> hashy = new HashMap<String, ArrayList<String>>();
String key = "Your key";
ArrayList<String> yourarraylist = hashy.get(key);
for(String valuessaved2key : yourarraylist){
System.out.println(valuessaved2key);
}
}
you could always do something like this and create yourself a maze!
public void LOOK_AT_ALL_THESE_HASHMAPS(){
HashMap<String, HashMap<String, HashMap<String, HashMap<String, String>>>> theultimatehashmap = new HashMap <String, HashMap<String, HashMap<String, HashMap<String, String>>>>();
String ballsdeep_into_the_hashmap = theultimatehashmap.get("firststring").get("secondstring").get("thirdstring").get("forthstring");
}
BTW, if you want some semantics such as only put if this key is not exist. you can use concurrentHashMap with putIfAbsent() function.
Check this out:
https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/ConcurrentHashMap.html#put(K,%20V)
concurrentHashMap is thread safe with high performance since it uses "lock striping" mechanism to improve the throughput.
Yes, this means all the 1 keys with value are overwriten with the last added value and here you add "surely not one" so it will display only "surely not one".
Even if you are trying to display with a loop, it will also only display one key and value which have same key.
HashMap<Emp, Emp> empHashMap = new HashMap<Emp, Emp>();
empHashMap.put(new Emp(1), new Emp(1));
empHashMap.put(new Emp(1), new Emp(1));
empHashMap.put(new Emp(1), new Emp());
empHashMap.put(new Emp(1), new Emp());
System.out.println(empHashMap.size());
}
}
class Emp{
public Emp(){
}
public Emp(int id){
this.id = id;
}
public int id;
#Override
public boolean equals(Object obj) {
return this.id == ((Emp)obj).id;
}
#Override
public int hashCode() {
return id;
}
}
OUTPUT : is 1
Means hash map wont allow duplicates, if you have properly overridden equals and hashCode() methods.
HashSet also uses HashMap internally, see the source doc
public class HashSet{
public HashSet() {
map = new HashMap<>();
}
}

Categories