I found this program in my text book, which basically counts the occurence of each string in the String array tst.
public class Test {
private static HashMap<String, Integer> mp = new HashMap<String, Integer>();
public static void main(String[] args) {
String[] tst = new String[] { "ABC", "DEF", "DEF", "DEF","ABC", "DEF", "ABC" };
checkMap(tst);
}
public static void checkMap(String[] str) {
for (String st : str) {
if (!mp.containsKey(st)) {
mp.put(st, 1);
}
else {
Integer ct = mp.get(st);
if(ct!=null)
{
ct++;
mp.put(st, ct);
}
}
}
for (Map.Entry<String, Integer> entry : mp.entrySet()) {
System.out.println(entry.getKey() + " ocurrs " + entry.getValue()+ " times");
}
}
}
The output for the code is -
ABC ocurrs 3 times
DEF ocurrs 4 times
My question is in the if/else statement here -
if (!mp.containsKey(st)) {
mp.put(st, 1);
}
else {
Integer ct = mp.get(st);
if(ct!=null)
{
ct++;
mp.put(st, ct);
}
}
When we haven't put any entries inside the hashmap (the hashmap is empty), on what basis does this work? Apologies if this is a very basic question, but I found no answer anywhere online that explains this. I am confused with what is written in the if/else loop.
Also, this line here -
Integer ct = mp.get(st);
How can we get the value to which the key is mapped when infact the hashmap is actually empty? I am trying to relate this to an array - If you query elements of an array once its created, but not initialized, it throws a null pointer. Someone, please explain how this works for a hashmap. Once again, apologies for asking such a basic question.
Well, in this line you check whether the map contains a key
if (!mp.containsKey(st)) {
Since there is a ! before the expression, this means "if the map does not contain a key". After that, "then" block follows where you insert a key in the map with value 1 (since it does not exist).
Otherwise if the key does exist (the else block), you take the value for that key, increment it (ct++) and add it again to the map for the same key.
Let me just say that the null check (if(ct!=null)) is not necessary for this code.
General remark on this question:
How can we get the value to which the key is mapped when infact the hashmap is actually empty?
If you try to get something from the HashMap for a key that is not present in the map, the map returns null. That is true for any key you try to get from an empty map.
Can you please explain what this means though - Integer ct = mp.get(st);
map.get(key) returns a value that is stored for that key. The map itself is a collection of key-value pairs, which means: for each key there is one value in the map. So to get the value stored for that key you invoke map.get(key). If you store map.put("ABC", 10) the map will return 10 for map.get("ABC").
This is because of containsKey function checks if the hashMap contains particular key.
If the HashMap is mpty and you try to do a get on non existant key you will get a null value
st is get here by the for (String st : str) loop. It has nothing to do with the HashMap.
if (!mp.containsKey(st)) {
This tests if the HashMap does not contain the key st. If there are no items it obviously can not contain the key. Then in the else block it uses mp.get(st), which will now always succeed because it has been checked that mp contains st (actually, it does not not contain it).
The null check if (ct == null) is here because if for some reason the map contained null for the key in question. That however shouldn't be possible if the code only puts integers to the map and tests for the existence of the key, so the null check coulb be removed.
The test:
if (!mp.containsKey(st))
tests if there is no entry in the map by that key.
It is therefore logical that in the else branch, the entry exists and has a non null value... Which makes the ct == null test redundant.
And when the value exists, the code get()s the existing value, adds 1 to it (in fact it creates a new Integer but that's another story) and put()s back the new value.
Note that that code mixes autoboxing and non autoboxing. mp.put(st, 1) does autoboxing; behind the scenes it really does mp.put(st, new Integer(1)).
Similarly:
Integer ct = mp.get(st);
ct++;
is really:
Integer ct = mp.get(st);
Integer tmp = new Integer(ct.intValue() + 1);
ct = tmp;
The null check is not necessary. Either the key is contained in the map and its value is not null, or it is not contained in the map.
The reason we can be confident about the value never being null is that the map (and all its contents) is defined and used in the method, and there's no opportunity for a null to get it there.
Although the get() method will return null if passed a key that it doesn't contain, that will never happen with this code.
Anyway, the code is inelegant: All those lines can be expressed as one simple line:
mp.put(mp.containsKey(st) ? mp.get(st) + 1 : 1);
Related
This question already has answers here:
Increment an Integer within a HashMap
(13 answers)
Closed 10 months ago.
I'm new to HashMaps, and I was wondering if there was a way to add values together if they have the same key.
For example, when I have the key 'a' and the value is 20 and later on I use the key 'a' again and the value is 10 the value should now be 30.
I don't know how I would check if the Value already is in the HashMap and then use the same value again. The adding part could be done with just a variable that copies the current value and adds the new one, I guess.
I am guessing you want a hashmap for character vs integer and want to add the integer to already present value in case the value is present.You can do something like below:
public hashMapImpl(char ch, int number){
Map<Character,Integer> map = new HashMap<>();
if(map.containsKey(ch)){
map.put(ch, map.get(ch)+number);
}
else{
map.put(ch,number);
}
}
Where ch will be your key and number will be something that you want to store at particular key.
you can use compute method to add/sum a value in case a key exists, or create a new entry in case it doesn't.
Map<String, Integer> map = new HashMap<>();
String myKey = "a";
Integer myValue = 10;
map.compute(myKey, (key, value) -> {
if (value == null)
return myValue;
else
return value + myValue;
}
);
System.out.println(map);
map.compute(myKey, (key, value) -> {
if (value == null)
return myValue;
else
return value + myValue;
}
);
System.out.println(map);
Outputs:
a={10}
a={20}
Of course, putting this compute logic inside a method will make you code cleaner :)
I will use an example class called HashMapExamle to explain this.
Inside the class I will create a HashMap called bigHashMap assuming that was your initial hashMap with prior data.
I also have a main method to call the method that will solve your problem. I have named the method bigHashMap. So for starters, i have initialized our prior HashMap named bigHashMap and initialized wit with some new key-value pairs.
I will printout the value of the HashMap. I will then add a value with the same key, in this case food with the value 15, by calling the hashMapValueAdd method I have created and passing the key(food) and value(15) to it.
The concept of the method is:
Check if the HashMap contains the key.
if it has it, it will get the value with that ky and add the new value to it. it will then replace the value at the key with the new value.
If the key is not found in the hashmap then the new key-value pair will just be inserted.
class HashMapExample {
private static HashMap<String, Integer> bigHashMap;
public static void main(String[] args) {
bigHashMap = new HashMap<>();
// Add test data
bigHashMap.put("food", 200);
bigHashMap.put("transport", 20);
bigHashMap.put("entertainment", 40);
System.out.println("Data before : \n" + bigHashMap);
hashmapValueAdd("food", 15);
System.out.println("Data after : \n" + bigHashMap);
}
private static void hashmapValueAdd(String key, int value) {
// Check if hashMap contains the given key
if (bigHashMap.containsKey(key)) {
// Get previous value with the same key
int valueWithSameKey = bigHashMap.get(key);
// Increment the value with incoming value
int newValue = valueWithSameKey + value;
// Put updated value into HashMap
// bigHashMap.put(key, +value);
bigHashMap.replace(key, newValue);
} else {
// Put the current key since it does not exist in the HashMap
bigHashMap.put(key, value);
}
}
}
I hope this solves your problem.
You can check out these HashMap explanations to get a better understanding.
Java HashMap - W3Schools
Java MAp - Jakob Jenkov
Java HashMap replace()
Update the Value Associated With a Key in a HashMap
There might be a silly mistake that I am unable to note, but in the code below whenever I put a new key,value pair, it replaces all other values for existing keys as well. I did a check to see if it already contains the key, but the check map.containsKey() always returns false as it should, then what is wrong here?
while ((line=reader.readLine())!=null){
String[] DZs;
if (id%2==0){
String[] values=line.split(" ");
String[] low=values[0].replace("[","").replace("]","").split(",");//lower limit array of subs
String[] high=values[1].replace("[", "").replace("]","").split(",");//upper limit of subs
assert low.length==high.length;
int[] lowdim=new int[low.length];
int[] highdim=new int[high.length];
for(int i=0;i<low.length;i++){
lowdim[i]=Integer.parseInt(low[i].trim());
highdim[i]=Integer.parseInt(high[i].trim());
}
lowerBound=lowdim;
upperBound=highdim;
id++;
}
else{
id++;
DZs=line.split(" ");
if (!subDzs.isEmpty()){
subDzs.clear();
}
for(String dz:DZs){
subDzs.add(dz);
}
Participant sub=new Participant(lowerBound,upperBound);
allSubs.add(sub);
System.out.println("Map contains key? " +subToDz.containsKey(sub));//returns false
subToDz.put(sub,subDzs);//overwrites existing values everytime new key,value is put
}
}
I'm not sure what subDzs is. Probably a List. Anyway, you are using the same value object for all your put statements. Each time you call subDzs.clear(), you are clearing the values of all the entries in your map. You should assign a new instance (of whatever type it is) to subDzs before putting it in the Map.
Replace
if (!subDzs.isEmpty()){
subDzs.clear();
}
for(String dz:DZs){
subDzs.add(dz);
}
Participant sub=new Participant(lowerBound,upperBound);
allSubs.add(sub);
System.out.println("Map contains key? " +subToDz.containsKey(sub));//returns false
subToDz.put(sub,subDzs);//overwrites existing values everytime new key,value is put
with
subDzs = new ... // create a new instance
for(String dz:DZs){
subDzs.add(dz);
}
Participant sub=new Participant(lowerBound,upperBound);
allSubs.add(sub);
System.out.println("Map contains key? " +subToDz.containsKey(sub));
subToDz.put(sub,subDzs);
Somebody please tell me the function containsKey() of HashMap that how does it work internally. does it use equals or hashcode function to match the key. I am using string keys for a hashmap and when I am using the key dynamically the containskey returns false. e.g. (Just a sample code not the original one I am using in application)
class employee {
employee(String name) {
return name;
}
}
class test {
HashMap hm = new HashMap();
hm.put("key1",new Employee("emp1"));
hm.put("key2",new Employee("emp2"));
hm.put("key3","emp4");
hm.put("new Employee("emp5")","emp4");
System.out.println(hm.containsKey("emp5"));
}
The key is an Employee object, not a string, in containsKey you have a string. That comparison will return false, because string "emp5" is not equal to an object Employee.
Here is a quote from containsKey doc:
Returns true if this map contains a mapping for the specified key. More formally, returns true if and only if this map contains a mapping for a key k such that (key==null ? k==null : key.equals(k))
Since in your case key is a string, 'equals' will return 'true' only if k is a string as well and its content is the same as that of key.
Your code has many errors, this is invalid hm.put("new Employee("emp5")","emp4");
Also use generic types with collections
HashMap<String,employee> hm = new HashMap<String,employee>();
And name you class Employee not employee , Begin with capital for class names. Also you are calling new Employee Whereas you classname is employee.
According to the source for hashMap . It calls equals() on the keys (in your case which would mean equals for String) internally
public boolean containsKey(Object key)
{
int idx = hash(key);
HashEntry<K, V> e = buckets[idx];
while (e != null)
{
if (equals(key, e.key))
return true;
e = e.next;
}
return false;
}
Your valid code (assuming you are not trying to achieve something unusual) should look like this :-
class Employee {
String name;
Employee(String name) {
this.name = name;
}
}
class Test {
public void hello() {
HashMap<String,Employee> hm = new HashMap<String,Employee>();
hm.put("key1", new Employee("emp1"));
hm.put("key2", new Employee("emp2"));
hm.put("key3", new Employee("emp4"));
hm.put("key4", new Employee("emp5"));
System.out.println(hm.containsKey("key4"));
}
}
Corrected Code:
HashMap hm= new HashMap();
hm.put("key1",new Employee("emp1"));
hm.put("key2",new Employee("emp2"));
hm.put("key3","emp4");
System.out.println(hm.containsKey("key1"));
This will return true.
You are saving Employee object against String keys. So you need to check the valid key. In your case emp5 is not used as a key while adding elements to hashmap.
For your second question:
It internally checks hashcode of the key first. If hashcodes are same it will check equals method.
Assuming
employee(String name) {
return name;
}
Is not a constructor and it is some method this piece of code will not compile. As you are returning String but you dint specify the return type in the method.
Moreover this line hm.put("new Employee("emp5")","emp4");
you have specified the key as
new Employee("emp5") and you are searching using the key emp5 in the containsKey() obviously it will return false because
containsKey() -Returns true if this map contains a mapping for the specified key.
Internally, a hash map can be implemented with an array of linked lists.
The key is passed to a routine (the hash) which gives back a number. The number is then divided by the size of the array, giving a remainder. That remainder is the linked list you then travel to see if any of the nodes exactly matches the key.
The advantages are that if you have a properly balanced hash function, and (let's say) an array of 32 items, you can quickly discard the searching of 31/32 (or +90%) of your possible values in a constant time operation.
Other means of implementation exist; however, they are computationally similar.
An example of a (very bad) hash algorithm for Strings might be to simply add up all the ASCII character values. Very good hash algorithms tend to give back an evenly distributed number based on the expected inputs, where incremental inputs do not incrementally fill adjacent buckets.
So, to find out if a hash map contains a key, get the result of the hash function on the key, and then walk down the correct linked list checking each entry for the key's presence.
In C, a "node" in the linked list.
struct Node {
char* key;
char* value;
struct Node* next;
};
In C, the "hashmap"
struct HashMap {
int size;
struct Node* listRoots;
};
The algorithm
int containsKey(HashMap* hashMap, char* key) {
int hash = hashFunc(key);
Node* head = hashMap->listRoots[hash % hashMap->size];
while (head != 0) {
if (strcmp(head->key, key)) {
return TRUE;
}
}
return FALSE;
}
Keep in mind my C is a bit rusty, but hopefully, you'll get the idea.
So I have a hashmap which contains key as Strings and value as Integers of the count of those strings occurring in my Set
for eg I would have a hashMap as follows
Key Value
abcd 4 (meaning there are 4 duplicate strings of abcd in my Set defined someplace)
----- 13
b-b- 7
and so on..
Now what I am trying to do is remove all the empty strings entries from my HashMap. So in the above example I would want to remove all the empty strings with value 13. So my resulting HashMap would be
Key Value
abcd 4
b-b- 7
This is my code that tries to do the same. generateFeedbackMap() is function which returns the HashMap in consideration StringIterator is a class which I have defined which iterates over through each character of my Strings.
for(String key : generateFeedbackMap().keySet()) {
StringIterator it = new StringIterator(key);
int counter = 0;
while(it.hasNext()){
String nextChar = it.next();
if(nextChar.equals("-")){
counter++;
}
Iterator<Map.Entry<String, Integer>> mapIterator = generateFeedbackMap().entrySet().iterator();
if(counter >= key.length()){
while(mapIterator.hasNext()){
Map.Entry<String, Integer> entry = mapIterator.next();
if(entry.getKey().equals(key)){
mapIterator.remove();
}
}
}
}
}
So I increment the counter wherever I find a "-" character. When the counter equals my key string length which means it is an empty string, I remove it using Map Iterator but this does not remove the entry from my Map. What am I doing wrong?
generateFeedbackMap() makes it sound like you’re getting a copy of the underlying map, in which case removing a key from the copy won’t affect the underlying map. If you’re actually getting the map, then you should rename your method.
Regardless, the following would accomplish the same as your original code (but will only remove from the copy).
Map<String,Integer> feedbackMap = generateFeedbackMap();
for ( String key : feedbackMap.keySet() ) {
if ( key.matches("-+") ) {
feedbackMap.remove(key);
}
}
If you’re stuck getting a copy of the underlying map, then you do need to create your new helpfulMap. But you can still use a regular expression and other Map functions to speed things up:
Map<String,Integer> helpfulMap = new HashMap<>();
for ( Map.Entry<String,Integer> entry : generateFeedbackMap().entrySet() ) {
if ( ! entry.getKey().matches("-+") ) {
helpfulMap.put(entry.getKey(),entry.getValue());
}
}
Okay guys, I think I figured out a solution. I just copied all my current entries from oldMap to a new defined HashMap which would contain at least one letter in their keys. So essentially I got rid of all the removing and iterating over strings and just use another HashMap instead as below
Map<String, Integer> HelpfulMap = new HashMap<String,Integer>();
for(String key : generateFeedbackMap().keySet()) {
StringIterator it = new StringIterator(key);
while(it.hasNext()){
String nextChar = it.next();
if(!nextChar.equals("-")){
HelpfulMap.put(key, generateFeedbackMap().get(key));
}
}
}
I don't know what I was doing previously. I went for a good shower and came up with this idea and it worked. I love programming!
Thanks everyone for your inputs!
I have an hashmap declared as
private HashMap testMessages = null;
I will be storing string values in both key and value part of the hashmap retrieved from oracle table.
I am not concerned about the hashmap keys. I want to retrieve the hashmap values alone and check whether string variable filename is prefixed with one of the hash map value and return true if it's same. I want to ensure that hash map values are not null and empty.
function (string filename)
{..
loop thru hashmap values
check whether the variable file name is prefixed with one of the hashmap values if so
return true
otherwise
return false
}
hashmap example:
key1,prod
key2,test
key3,dummy
filename example:
test123_20012010.csv
should return true since the file name is prefixed with one of the hashmap values
How can i do it?
for (String prefix : map.values()) {
if (filename.startsWith(prefix)) {
return true;
}
}
return false;
It should be noted that this is linear time in the number of entries in the map in the worst case. If you have multiple filename that you want to do the check for, it's much better to preprocess the prefixes and build something like a patricia trie and other fast dictionary-like data structures.
Here's a brute force approach to iterate over the hash map values and check whether filename begins with the value.
// generics version
private HashMap<String, String> testMessages = buildMap();
for (String v : testMessages.values()) {
if (filename.startsWith(v) {
// found a map value that starts the file name
}
}
// alternative non-generics version
private HashMap testMessages; // assigned somewhere
for (Object v : testMessages.values()) {
if (filename.startsWith((String) v) {
// found a map value that starts the file name
}
}
leeched from leepoint.net
public static void iterate_over_hashmap(Map mp) {
Iterator it = mp.entrySet().iterator();
while (it.hasNext()) {
Map.Entry pairs = (Map.Entry)it.next();
System.out.println(pairs.getKey() + " = " + pairs.getValue());
}
}
You have to treat each entry as a key/value pair and iterate over those as a single entity. Then you cast it into Map.Entry and then you can read both separately
function(String fileName)
{
for(String value : hashMap.values())
{
if(fileName.startsWith(value))
return true;
}
return false;
}