Adding new Key Value Pair Replaces all values in Java HashMap - java

I have a HaspMap which has "Text" objects as the key and "Integer" Object as Values. The Value is actually the number of Occurrences of the Key in my code. So for the first time it will be 1 and then it keeps incrementing. The Code is shown below.
First I check if a given "Text" Object exists in the Map. If it does not then I add it as a Key to the Map along with a Value of 1. But the problem that I am facing is that when I add the new Key and value to the map through "put" function for some reason all the previously present Key/value pairs in the Map are getting replaced with the new One. The code is given below.
public class WordPatternReducer extends Reducer<IntWritable,Text, Text, IntWritable>{
private IntWritable totalWordCount = new IntWritable();
private Map<Text,Integer> valueCount=new HashMap<Text,Integer>();
private Map<IntWritable,HashMap<Text,Integer>> posMap=new HashMap<IntWritable, HashMap<Text, Integer>>();
#Override
public void reduce(IntWritable key, Iterable<Text> values, Context context)
throws IOException, InterruptedException {
Iterator<Text> it=values.iterator();
Integer maxCountInteger=new Integer(1);
Text maxOccurText = null;
Text newval=new Text();
while (it.hasNext()) {
newval=it.next();
System.out.println("The new val outside is"+newval);
if (valueCount.containsKey(newval)) {
System.out.println("The new val inside if is"+newval);
valueCount.put(newval, (valueCount.get(newval)+1));
} else {
System.out.println(newval);
valueCount.put(newval, 1);
System.out.println(valueCount.toString());
}
maxOccurText=newval;
} }}
So the check for existing keys is working as it always go to the else statement. But the values are getting replaced. In the output console I am getting the following output.
The new val outside isWelcome
Welcome
{Welcome=1}
The new val outside isservice service
{service=1, service=1}
The new val outside isversions versions
{versions=1, versions=1, versions=1}
The new val outside isto
to
{to=1, to=1, to=1, to=1}
The new val outside isproviders,
providers,
{providers,=1, providers,=1, providers,=1, providers,=1, providers,=1}
The new val outside isof of
{of=1, of=1, of=1, of=1, of=1, of=1}
The new val outside isthe the
{the=1, the=1, the=1, the=1, the=1, the=1, the=1}
The new val outside issome some
{some=1, some=1, some=1, some=1, some=1, some=1, some=1, some=1}
and so on..
I do not want this. I just want to add the new Key value pairs retaining old one. Could someone please let me know what I have done wrong? Thank you in advance.

You're storing a reference to newval in the map. The containsKey(newval) checks false because Text checks to see if its contents are the same, but then the put call stores a reference to same newval that you've put in the map repeatedly (which contains only the latest string read in). Try changing the map to a map of String,Int and calling map.put(newval.toString()) to start, which should lead you to a better solution. Else, declare newval within the iterator loop so that you're storing a new Text object in the map each time (Text newval = it.next()).

What class is Text?
Hypothesis 1: Text doesn't implement equals() and hashCode() according to the contract. That'll mess up a map.
Hypothesis 2: Text is mutable and changing during the iteration. Using String keys in the map would fix that.

It looks like your Text value is mutable. What is the implementation of the Iterable values object? Is it a parser that repeatedly returns the same token instance, modifying its type and text with each call to next()? If so, you'll need to copy the text to a new immutable object—which might simply be a String—and use that immutable token as your key.

Related

Deleting a value only from a key on HashMap

I have set up a HashMap which populates with customers details as values (name, postcode, item) and the keys being say Customer1, Customer2 etc.
I want a method to delete just value "item" from each key, my code at the moment is as per below but when i run it the "item" value from argument isn't deleted.
public void deleteThisValue(String value)
{
if (this.customer.containsValue(value))
{
this.customer.remove(value);
}
}
Now in my head this works but it obviously doesn't, can anyone shed any light on this matter?
Thanks
You can simply iterate over the values...then invoking the get method of the map will return the reference of the value, on that reference you can invoke a setter.
myMap.get(x).setItem("newItem");
//or
myMap.get(x).setItem(-1);
it depends what is Item for a type....
example:
Map<String, Pojo> myMap = new HashMap<>();
myMap.put("A", new Customer2());
myMap.put("B", new Customer2());
myMap.put("C", new Customer2());
for (String x : myMap.keySet()) {
myMap.get(x).setItem("newItem");
//or
myMap.get(x).setItem(-1);
}
System.out.println(myMap);
Edit:
since java8 is offering streams, you can use those nice features doing:
myMap.values().stream().forEach(x -> x.setItem("none"));
remove method works with key only,
If you want to remove value only, then you can set value as null for that key.
e.g:
this.customer.put(key,null);
But if you want to set a particular value (say item) of value then you can do this something like that:
this.customer.get(key).setItem(null);

MapWritable replacing ALL existing keys with newly added ones - Hadoop MapReduce 2.6.4

I am working with Hadoop 2.6.4 and I am trying to implement a Stripes mapper for word Co-Occurances. I am running into an issue when attempting to use MapWritable class. When trying to add new key/values into the map, any key that is added is replacing every single other key in the map with itself.
For example, let's say I have a sentence like
"This is a sentence with two a letters"
The first run through, I am looking at the co-occurrences for the word "This". So the expected mapper would be
<is,1>
<a,2>
<sentence,1>
<with,1>
<two,1>
<letters,1>
But what is actually happening is on each iteration of adding the subsequent words, ALL keys/values are being replaced with the last key that was added. The actual result I am seeing is the following.
<letters,1>
<letters,1>
<letters,1>
<letters,1>
<letters,1>
<letters,1>
I have created a method to convert a HashMap to MapWritable, this is where the issue is occurring. Here is the code I am using. I have added print statements to make sure the values I am adding are correct (they are) and then I am printing the keys to see what is occurring as I am adding them. This is where I was able to see that it is replacing each key as it adds a new one.
According to all documentation I have looked at, I am using MapWritable.put() properly, and it should simply be adding to the map or updating the value, as it would with a generic HashMap. I am at a loss as to what is causing this.
public static MapWritable toMapWritable(HashMap<String,Integer> map){
MapWritable mw = new MapWritable();
Text key = new Text();
IntWritable val = new IntWritable();
for(String it : map.keySet()){
key.set(it.toString());
System.out.println("Setting Key: " + key.toString());
val.set(map.get(it));
System.out.println("Setting Value: " + map.get(key.toString()));
mw.put(key,val);
for(Writable itw : mw.keySet()){
System.out.println("Actual mw Key " + itw.toString());
}
}
return mw;
}
You are calling key.set() repeatedly and you have only allocated one Text. This is basically what you are doing.
Text key = new Text();
key.set("key1");
key.set("key2");
System.out.println(key); // prints 'key2'
I believe you might be implementing the common pattern of reusing objects in a Map/Reduce job. However, that hinges upon calling context.write(). For instance:
private Text word = new Text();
private IntWritable count = new IntWritable(1);
public void map(LongWritable offset, Text line, Context context) {
for (String s : line.toString().split(" ")) {
word.set(s);
context.write(word, count); // Text gets serialized here
}
}
In the above example, the Map/Reduce framework will serialize that text to bytes and save them behind the scenes. That's why you are free to reuse the Text object. MapWritable does not do the same thing however. You need to create new keys each time.
MapWritable mw = new MapWritable();
mw.put(new Text("key1"), new Text("value1"));
mw.put(new Text("key2"), new Text("value2"));

My arraylist is only outputting the last value

I created a HashMap to store a text file with the columns of information. I compared the key to a specific name and stored the values of the HashMap into an ArrayList. When I try to println my ArrayList, it only outputs the last value and leaves out all the other values that match that key.
This isn't my entire code just my two loops that read in the text file, stores into the HashMap and then into the ArrayList. I know it has something to do with my loops.
Did some editing and got it to output, but all my values are displayed multiple times.
My output looks like this.
North America:
[ Anguilla, Anguilla, Antigua and Barbuda, Antigua and Barbuda, Antigua and Barbuda, Aruba, Aruba, Aruba,
HashMap<String, String> both = new HashMap<String, String>();
ArrayList<String> sort = new ArrayList<String>();
//ArrayList<String> sort2 = new ArrayList<String>();
// We need a try catch block so we can handle any potential IO errors
try {
try {
inputStream = new BufferedReader(new FileReader(filePath));
String lineContent = null;
// Loop will iterate over each line within the file.
// It will stop when no new lines are found.
while ((lineContent = inputStream.readLine()) != null) {
String column[]= lineContent.split(",");
both.put(column[0], column[1]);
Set set = both.entrySet();
//Get an iterator
Iterator i = set.iterator();
// Display elements
while(i.hasNext()) {
Map.Entry me = (Map.Entry)i.next();
if(me.getKey().equals("North America"))
{
String value= (String) me.getValue();
sort.add(value);
}
}
}
System.out.println("North America:");
System.out.println(sort);
System.out.println("\n");
}
Map keys need to be unique. Your code is working according to spec.
if you need to have many values for a key, you may use
Map<key,List<T>>
here T is String (not only list you can use any collection)
Some things seems wrong with your code :
you are iterating on the Map EntrySet to get just one value (you could just use the following code :
if (both.containsKey("North America"))
sort.add(both.get("North America"));
it seems that you can have "North America" more than one time in your input file, but you are storing it in a Map, so each time you store a new value for "North America" in your Map, it will overwrite the current value
I don't know what the type of sort is, but what is printed by System.out.print(sort); is dependent of the toString() implementation of this type, and the fact that you use print() instead of println() may also create problems depending on how you run your program (some shells may not print the last value for instance).
If you want more help, you may want to provide us with the following things :
sample of the input file
declaration of sort
sample of output
what you want to obtain.

adding a key to HashMap without the value?

Is there a way to add a key to a HashMap without also adding a value? I know it seems strange, but I have a HashMap<String, ArrayList<Object>> amd I want to first be able to create keys as needed and then check if a certain key exists and, if so, put the appropriate value, namely the ArrayList<Object>
Was that confusing enough?
Since you're using a Map<String, List<Object>>, you're really looking for a multimap. I highly recommend using a third-party library such as Google Guava for this - see Guava's Multimaps.
Multimap<String, Object> myMultimap = ArrayListMultimap.create();
// fill it
myMultimap.put("hello", "hola");
myMultimap.put("hello", "buongiorno");
myMultimap.put("hello", "สวัสดี");
// retrieve
List<String> greetings = myMultimap.get("hello");
// ["hola", "buongiorno", "สวัสดี"]
Java 8 update: I'm no longer convinced that every Map<K, SomeCollection<V>> should be rewritten as a multimap. These days it's quite easy to get what you need without Guava, thanks to Map#computeIfAbsent().
Map<String, List<Object>> myMap = new HashMap<>();
// fill it
myMap.computeIfAbsent("hello", ignored -> new ArrayList<>())
.addAll(Arrays.asList("hola", "buongiorno", "สวัสดี");
// retrieve
List<String> greetings = myMap.get("hello");
// ["hola", "buongiorno", "สวัสดี"]
I'm not sure you want to do this. You can store null as a value for a key, but if you do how will be able to tell, when you do a .get("key") whether the key exists or if it does exist but with a null value? Anyway, see the docs.
You can put null values. It is allowed by HashMap
You can also use a Set initially, and check it for the key, and then fill the map.
Yes, it was confusing enough ;) I don't get why you want to store keys without values instead just putting empty arraylists instead of null.
Adding null may be a problem, because if you call
map.get("somekey");
and receive a null, then you do not know, if the key is not found or if it is present but maps to null...
//This program should answer your questions
import java.util.*;
public class attemptAddingtoHashMap { //Start of program
//MAIN METHOD #################################################
public static void main(String args[]) { //main begins
Map<String, ArrayList<Object>> hmTrial = new HashMap<String, ArrayList<Object>>();
ArrayList alTrial = new ArrayList();//No values now
if (hmTrial.containsKey("first")) {
hmTrial.put("first", alTrial); }
else {hmTrial.put("first",alTrial);}
//in either case, alTrial, an ArrayList was mapped to the string "first"
//if you choose to, you can also add objects to alTrial later
System.out.println("hmTrial is " + hmTrial); //empty now
alTrial.add("h");
alTrial.add("e");
alTrial.add("l");
alTrial.add("l");
alTrial.add("o");
System.out.println("hmTrial is " + hmTrial);//populated now
} //end of main
//#############################################################################################################
} //end of class
//Note - removing objects from alTrial will remove the from the hashmap
//You can copy, paste and run this code on https://ide.geeksforgeeks.org/

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