Counting occurrences of a key in a Map in Java - java

I'm writing a project that captures Java keywords from a .java file and keeps track of the occurrences with a map. I've used a similar method in the past successfully, but I can't seem to adopt this method for my intended use here.
Map<String,Integer> map = new TreeMap<String,Integer>();
Set<String> keywordSet = new HashSet<String>(Arrays.asList(keywords));
Scanner input = new Scanner(file);
int counter = 0;
while (input.hasNext())
{
String key = input.next();
if (key.length() > 0)
{
if (keywordSet.contains(key))
{
map.put(key, 1);
counter++;
}
if(map.containsKey(key)) <--tried inner loop here, failed
{
int value = map.get(key);
value++;
map.put(key, value);
}
}
This block of code is supposed to add the keyword to the key, and increment the value each time the same key occurs. So far, it adds the keywords, but fails to properly increment the value. here is a sample output:
{assert=2, class=2, continue=2, default=2, else=2, ...}
Basically it increments every value in the map instead of the ones it's supposed to. I'm not sure if I'm over-thinking this or what. I've tried an inner loop and it gave me insane results. I really hope I'm just over-thinking this. Any help is greatly appreciated!

There's a much more concise (and easier to reason about) way to achieve what you want:
final ConcurrentMap<String, AtomicInteger> map = new ConcurrentHashMap<>();
final Scanner input = new Scanner(file);
while (input.hasNext()) {
final String key = input.next();
if (key.length() > 0) {
map.putIfAbsent(key, new AtomicInteger(0));
map.get(key).incrementAndGet();
}
}
Let's analyze why does this work.
Whenever the Scanner encounters a keyword, there are 2 possible cases: you either have encountered it before (ie, it is a known keyword), or it is an yet unseen keyword.
If it is an unseen keyword: putIfAbsent will put an AtomicInteger with value 0 in the map, and incrementAndGet() will set it to 1 right after, and, from now on, it becomes a known keyword;
If it is a known keyword: putIfAbsent will do nothing, and incrementAndGet() will increment the value that is already present in the map.
Then, if you want the key set, you do:
final Set<String> keys = map.keySet();
To print all the values, you could do something like:
for (final String k : map.keySet()) {
System.out.println(k + ": " + map.get(k).get());
}
You are not forced to use the two "different" classes I used above, ConcurrentMap and AtomicInteger. It is just easier to use them because they encapsulate much of the logic that you tried to write by yourself (and failed). The logic that they encapsulate is exactly all the other answers describe (ie, test if the value is present, if not set it to 0, then get whatever value is present, increment it and put it back into the map).
To maintain the keys of the map (our words being counted) in alphabetical order, use a ConcurrentNavigableMap such as ConcurrentSkipListMap .

For every key you scan you create a new entry in the map (overriding the existing one). Then, the next condition holds so you increment the count by 1, reaching the value 2.
The inner part should be something like:
if (keywordSet.contains(key))
{
Integer value = map.get(key);
if (value == null)
value = 0;
value++;
map.put(key, value);
}
Anyway, consider using some kind of a mutable integer to make this more efficient. You won't have to override entries in the map, and you won't be doing too much Integer boxing operations.

Even more concise using Map.merge (since Java 8):
if (keywordSet.contains(key)) {
map.merge(key, 1, (currentCount, notUsed) -> ++currentCount);
}
Here is a generic implementation of a counting map - a map with values representing the count of their keys:
public static <K> void count(K key, Map<K, Integer> map) {
map.merge(key, 1, (currentCount, notUsed) -> ++currentCount);
}
public static void main(String[] args) {
Map<String, Integer> map = new HashMap<>();
count("A", map);
count("B", map);
count("A", map);
count("Z", map);
count("A", map);
System.out.println(map); // {A=3, B=1, Z=1}
}

You always set the value to 1 and then update it by another one. What you need is to update the map value (and not setting it to 1 again).
Instead of:
map.put(key, 1);
use:
Integer value = map.get(key);
if (value == null){
value = 0
}
value++;
map.put(key, value);
And drop the second if.

Map<String, Integer> map = new HashMap<String, Integer>();
Set<String> keywordSet = new HashSet<String>(Arrays.asList(keywords));
Scanner input = new Scanner(file);
while (input.hasNext()){
String key = input.next();
if (key.length() > 0)
if (keywordSet.contains(key)){
Integer counter = map.get(key);
if (counter == null)
map.put(key, 1);
else
map.put(key, count + 1);
}
}

map.compute(key, (k, value) -> (value == null) ? 1 : (value + 1));

Related

getting map key via value

i have this kind of data structure
Map<Integer, Integer> groupMap= new LinkedHashMap<>();
groupMap.put(10, 1);
groupMap.put(11, 0);
groupMap.put(14, 1);
groupMap.put(13, 0);
groupMap.put(12, 0);
groupMap.put(15, 1);
what can be the best way to find the key which has value 1 if i have a present key with one value.
Ex:i have key 14, now need to find the key 15 which has value 1
least looping will be helpfull.
my approch:
List<Integer> keys = new ArrayList<>();
keys.putAll(groupMap.keySet());
//getting the index of current key i have
int index = keys.indexOf(14);
if(keys.size() == index) return -1;
for(int i = index+1;i<keys.size();i++){
if(groupMap.get(i) == 1) return i;
}
i know it isn't a very good approach, but can you please suggest a good one.
This completely defeats the purpose of a key-value map. But if it's really what you want, I suppose you could do the following:
public static int getNextKeyByValue(int value, int previousKey) {
final Map<Integer, Integer> groupMap = new HashMap<>();
Iterator iterator = groupMap.entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry<Integer, Integer> entry = (Map.Entry<Integer, Integer>) iterator.next();
if (entry.getValue() == value && entry.getKey() != previousKey) {
return entry.getKey();
}
}
return -1;
}
From the topic which #Titus mentioned in the comment, the most elegant and shortest solution is to use stream:
int getFirstCorrectValueBiggerThan (int lastValue) {
return groupMap.entrySet().stream()
.filter(entry -> Objects.equals(entry.getValue(), 1))
.map(Map.Entry::getKey)
.filter(value -> value > lastValue)
.findFirst();
}
edit:
sorry for the mistake, the code provided does not solve your problem since it is comparing keys not indexes. Here you have proper version, however it is not so cool anymore.
ArrayList<Integer> filteredList = groupMap.entrySet().stream()
.filter(entry -> entry.getValue().equals(1))
.map(Map.Entry::getKey)
.collect(Collectors.toCollection(ArrayList::new));
int nextCorrectElement = filteredList.get(filteredList.indexOf(14) + 1);
update
as far as i undestand what is written in this tutorial about map:
When a user calls put(K key, V value) or get(Object key), the function computes the index of the bucket in which the Entry should be. Then, the function iterates through the list to look for the Entry that has the same key (using the equals() function of the key).
and check out this topic about hash map complexity.
O(1) certainly isn't guaranteed - but it's usually what you should assume when considering which algorithms and data structures to use.
On top of that, the key part of your solution- the ArrayList::indexOf- is O(N) complex- you have to iterate through each element till the one which meets the condition. More info is in this topic.
So efectively you are iterating through every element of your hashmap anyway. And what is more, the hashmap searching (get method) is not quaranteed to be O(1) complex so there is a chance that you will double your work.
I have made a simple test of performance for stream based solution and simple loop proposed in this topic. In fact loop will be faster than sequential stream for each case I think, but still if you want that kind of performance gain then try to write it in in C++. Otherwise if you have more complex example then using the parallel stream may get some advantage due to higher abstraction level of the problem stating.
I have not really clear your question. If you are looking for all the tuples with value equals to 1, you could follow the approach below:
for (Entry<Integer, Integer> entry : groupMap.entrySet()) {
if (entry.getValue() == 1) {
System.out.println("The key is: " + entry.getKey().toString());
}
}

How do you update all the values in a HashMap?

I am using processing, and I have a HashMap, in which I want to be able to increment all the values by 1.
The code looks like this:
HashMap<Character, Integer> keyStates = new HashMap<Character, Integer>();
void press(Character k) {
keyStates.put(k, 1);
}
void release(Character k) {
keyStates.put(k, 0);
}
And the Integer tells me how long I have been pressing a key. Thus, I want to increment all the values of this HashMap by one, regardless of key, every "tick" or frame. Is there a way to increment all Integer Values of a HashMap, or, if not, another way of getting around this issue. Thank you very much for your help. Also, this is my first post, so please tell me if I am doing it right.
The best way to do it would be by using Iterator :
Iterator<Map.Entry <Character, Integer> > it = keyStates.entrySet().iterator();
while (it.hasNext()) {
Map.Entry<Character, Integer> pair = it.next();
Integer newCount = (pair.getValue() == null) ? 1 : pair.getValue() + 1 ;
pair.setValue(newCount);
}

HashMap : Adding values with common keys and printing them out

I have file which has String in the form key/value pair like people and count, example would be
"Reggy, 15"
"Jenny, 20"
"Reggy, 4"
"Jenny, 5"
and in the output I should have summed up all count values based on key so for our example output would be
"Reggy, 19"
"Jenny, 25"
Here is my approach:
Read each line and for each line get key and count using scanner and having , as delimiter
Now see if key is already present before if then just add currentValues to previousValues if not then take currentValue as value of HashMap.
Sample Implementation:
public static void main(final String[] argv) {
final File file = new File("C:\\Users\\rachel\\Desktop\\keyCount.txt");
try {
final Scanner scanner = new Scanner(file);
while (scanner.hasNextLine()) {
if (scanner.hasNext(".*,")) {
String key;
final String value;
key = scanner.next(".*,").trim();
if (!(scanner.hasNext())) {
// pick a better exception to throw
throw new Error("Missing value for key: " + key);
}
key = key.substring(0, key.length() - 1);
value = scanner.next();
System.out.println("key = " + key + " value = " + value);
}
}
} catch (final FileNotFoundException ex) {
ex.printStackTrace();
}
}
Part I am not clear about is how to divide key/value pair while reading them in and creating HashMap based on that.
Also is the approach am suggestion an optimal one or is there a way to enhance the performance more.
Since this is almost certainly a learning exercise, I'll stay away from writing code, letting you have all the fun.
Create a HashMap<String,Integer>. Every time that you see a key/value pair, check if the hash map has a value for the key (use 'containsKey(key)'). If it does, get that old value using get(key), add the new value, and store the result back using put(key, newValue). If the key is not there yet, add a new one - again, using put. Don't forget to make an int out if the String value (use Integer.valueOf(value) for that).
As far as optimizing goes, any optimization at this point would be premature: it does not even work! However, it's hard to get much faster than a single loop that you have, which is also rather straightforward.
Try this:
Map<String, Long> map = new HashMap<String, Long>();
while (scanner.hasNextLine()) {
if (scanner.hasNext(".*,")) {
....
if(map.containsKey(key))
map.put(key, map.get(key) + Long.valueOf(value));
else
map.put(key, Long.valueOf(value));
}
}
Simplest way I can think about splitting the values:
BufferedReader reader = new BufferedReader(new FileReader(file));
Map<String, Integer> mapping = new HashMap<String,Integer>();
String currentLine;
while ((currentLine = reader.readLine()) != null) {
String[] pair = currentLine.split(",");
if(pair.length != 2){ //could be less strict
throw new DataFormatException();
}
key = pair[0];
value = Integer.parseInt(pair[1]);
if(map.contains(key)){
value += map.get(key);
}
map.put(key,value);
}
It is most likely not the most efficient way in terms of performance, but is pretty straightforward. Scanner is usually used for parsing, but the parsing here doesn't look as complex, is just a split of strings.
For reading in, personally, I'd use:
Scanner.nextLine(), String.split(","), and Integer.valueOf(value)
Kind of late but clean solution with time complexity of O(n). This solution bypasses sort of arrays
public class Solution {
public static void main(String[] args) {
// Anagram
String str1 = "School master";
String str2 = "The classroom";
char strChar1[] = str1.replaceAll("[\\s]", "").toLowerCase().toCharArray();
char strChar2[] = str2.replaceAll("[\\s]", "").toLowerCase().toCharArray();
HashMap<Character, Integer> map = new HashMap<Character, Integer>();
for (char c : strChar1) {
if(map.containsKey(c)){
int value=map.get(c)+1;
map.put(c, value);
}else{
map.put(c, 1);
}
}
for (char c : strChar2) {
if(map.containsKey(c)){
int value=map.get(c)-1;
map.put(c, value);
}else{
map.put(c, 1);
}
}
for (char c : map.keySet()) {
if (map.get(c) != 0) {
System.out.println("Not anagram");
}
}
System.out.println("Is anagram");
}
}
public Map<String, Integer> mergeMaps(#NonNull final Map<String, Integer> mapOne,
#NonNull final Map<String, Integer> mapTwo) {
return Stream.of(mapOne.entrySet(), mapTwo.entrySet())
.flatMap(Collection::stream)
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, Integer::sum));
}

Java code to Prevent duplicate <Key,Value> pairs in HashMap/HashTable

I have a HashMap as below (assuming it has 10,0000 elements)
HashMap<String,String> hm = new HashMap<String,String>();
hm.put("John","1");
hm.put("Alex","2");
hm.put("Mike","3");
hm.put("Justin","4");
hm.put("Code","5");
==========================
Expected Output
==========================
Key = John",Value = "1"
Key = Alex",Value = "2"
Key = Mike",Value = "3"
Key = Justin",Value = "4"
Key = Code",Value = "5"
===========================
I need Java code to prevent Addition of Duplicate <Key,Value> Pairs in HashMap such
that below conditions are staisfied.
1> hm.put("John","1"); is not accepted/added again in the Map
2> hm.put("John","2"); is not accepted/added again in the Map
Hope its clear.
Java code provided will be appreciated.(generic solution needed since i can add any duplicate to the existing map)
You can wrap HashMap in a class, which delegates put, get, and other methods you use from HashMap. This method is wasteful but safe, since it doesn't depend on the internal implementation of HashMap, AbstractMap. The code below illustrates put, get delegating:
public class Table {
protected java.util.HashMap<String, Integer> map =
new java.util.HashMap<String, Integer>();
public Integer get(String key) { return map.get(key); }
public Integer put(String key, Integer value) {
if (map.containsKey(key)) {
// implement the logic you need here.
// You might want to return `value` to indicate
// that no changes applied
return value;
} else {
return map.put(key, value);
}
}
// other methods goes here
}
Another option is to make a class which extends HashMap, and depend on its internal implementation. Java 1.6 sources shows that put is called only in putAll in HashMap, so you can simply override put method:
public class Table extends java.util.HashMap<String, Integer> {
public Integer put(String key, Integer value) {
if (containsKey(key)) {
// implement the logic you need here.
// You might want to return `value` to indicate
// that no changes applied
return value;
} else {
return super.put(key, value);
}
}
}
Another option is similar to the first, and can make an utility method in your class which contains the HashMap instance and call that method wherever you need put something to your map:
public final Integer putToMap(String key, String value) {
if(this.map.containsKey(key)) {
return value;
} else {
return this.map.put(key, value);
}
}
This is an "inline" equivalent of checking manually.
I note that you clarify the question by suggesting you might have "100000000 elements". You still won't have duplicates in the HashMap, because, as two other posters have pointed out, you can't get duplicate keys in a Map. I'm still not sure we understand the question, though, as it's not at all clear how you expected to generate the block titled "Output", or what you intend to do with it.
This may be old question but I thought to share my experience with this. As others pointed out you can't have the same element in a HashMap. By default HashMap will not allow this but there are some cases that you could end up with two or more elements are almost alike that you do not accept but HashMap will. For example, the following code defines a HashMap that takes an array of integers as a key then add :
HashMap<int[], Integer> map1 = new HashMap<>();
int[] arr = new int[]{1,2,3};
map1.put(arr, 4);
map1.put(arr, 4);
map1.put(arr, 4);
At this point, the HashMap did not allow dublicating the key and map1.size() will return 1. However, if you added elements without creating the array first things will be different:
HashMap<int[], Integer> map2 = new HashMap<>();
map2.put(new int[]{4,5,6}, 6);
map2.put(new int[]{4,5,6}, 6);
map2.put(new int[]{4,5,6}, 6);
This way, the HashMap will add all the three new elements so the map2.size() will return 3 and not 1 as expected.
The explanation is that with the first map I created the object arr once and tried to add the same object 3 times which HashMap does not allow by default so only the last usage will be considered. With the second map, however, evey time I recreate a new object on the stack. The three objects created are different and separated thought the three of them have the same data but they are different. That's why HashMap allowed them as different keys.
Bottom line, you don't need to prevent HashMap from adding dublicated keys because it won't by design. However, you have to watch out how you define these keys because the fault may be on your side.
List<String> keys = new ArrayList<String>(); (1000000)
List<String> values = new ArrayList<String>(); (1000000)
Map<String, String> map = new HashMap<String, String>();
int i =0;
for(String key : keys){
String returnedValue = map.put(key, values.get(i));
if(returnedValue!=null){
map.put(key, returnedValue);
system.out.println("Duplicate key trying to be entered with new value so reverting the duplicate key ="+key+"new Value"+values.get(i));
}
}
Unfortunately, it is the way that Map works.
The easiest workaround is to remove all pre existed keys and their values by calling hm.remove() first! like this:
for (String name : names) {
hm.remove(name);
hm.put(name,uri.getQueryParameter(name));
}
And if you don't use a for loop just call it like this:
hm.remove("John");
hm.put("John","1");
hm.remove("Alex");
hm.put("Alex","2");
hm.remove("Mike");
hm.put("Mike","3");
And so on ...
see even if u write same key values multiple times you will just have unique set of pairs. Check that by either iterating or by doing hm.size();
if(hm.put("John","1") != null)
{
// "John" was already a key in the map. The sole value for this key is now "1".
}
List<Object> yourElements = new ... // 10000000
for(Object O : yourElements) {
if(myMap.get(O.key)==null) {
myMap.put(O.key,O);
}
}

Increment an Integer within a HashMap

Do I have to return the object and then put a new one in ? Or can I just directly increment ?
Integer temp = myMap.get(key);
temp++;
myMap.put(key, temp);
there is no way to just do this (this doesn't work) :
myMap.get(key)++;
This is the shortest code that does this job.
myMap.put(key, myMap.get(key) + 1)
I think it is not too long.
In Java 8 there are new methods on Map which you can use with lambdas to solve this. First alternative, compute:
a.compute(key, (k, v) -> v+1);
Note that this only works if the hash is initialized for all possible keys.
If this is not guaranteed you can either change the above code to:
a.compute(key, (k, v) -> v == null ? 1 : v + 1);
Or use the merge method (which I would prefer):
a.merge(key, 1, (a, b) -> a + b);
Maybe there are more lambda based methods I am not aware of.
You can use a mutable integer such as AtomicInteger.
Map<Key, AtomicInteger> myMap = new HashMap<Key, AtomicInteger>();
myMap.get(key).incrementAndGet();
Or you can use Trove4j which supports primitives in collections.
TObjectIntHashMap<Key> myMap;
myMap.increment(key);
Do I have to return the object and then put a new one in ?
As long as you use the Integer wrapper class yes, because it's immutable. You could use a mutable wrapper class instead, even one that has an increment() method. However, you then lose the ability to use autoboxing and autounboxing on the values.
You can't directly increment it, because it is immutable. You have to increment it and put the new object back.
Auto boxing is also interfering here. In fact what's happening is something similar to:
Integer i1 = getFromMap();
i1 = Integer.valueOf(++ i1.intValue());
So here your reference points to a new object. You have to put that object back in the map, under the same key.
As Integer are immutable, yes, you have to do it that way.
If you really want to increment it directly, you'll have to write your own mutable class.
If you have to do this more than twice you'd prefer to create a tiny class like:
public class MappedCounter {
private Map<String, Integer> map = new HashMap<String, Integer>();
public void addInt(String k, int v) {
if (!map.containsKey(k)) map.put(k, v);
else map.put(k, map.get(k) + v);
}
public int getInt(String k) {
return map.containsKey(k) ? map.get(k) : 0;
}
public Set<String> getKeys() {
return map.keySet();
}
}
Here are solutions using a Map (Java 8+), and a primitive Map and Bag using Eclipse Collections (EC).
JDK Map
Map<String, Integer> map = new HashMap<>();
map.merge("item", 1, Integer::sum);
Integer count = map.getOrDefault("item", 0);
EC Primitive Map
MutableObjectIntMap<String> map = ObjectIntMaps.mutable.empty();
map.addToValue("item", 1);
int count = map.getIfAbsent("item", 0);
EC Bag
MutableBag<String> bag = Bags.mutable.empty();
bag.add("item");
int count = bag.occurrencesOf("item");
The benefit of the primitive Map or Bag (which wraps a primitive Map) is that there is no boxing of the count values, and adding is explicit in both method names (addToValue / add). A Bag is a better data structure IMO if you want to simply count things.
Note: I am a committer for Eclipse Collections.
First of all: be aware of unboxing: the temp is from type Integer. But the operation ++ is implemented for int. So temp is unboxed to type int. This means if temp is null you run in a NullPointerException.
And you have to do it like you discripted in your first code block.
I use the below code and it works but at the beginning you need to define a BiFunction describing that the operation is incrementing by 1.
public static Map<String, Integer> strInt = new HashMap<String, Integer>();
public static void main(String[] args) {
BiFunction<Integer, Integer, Integer> bi = (x,y) -> {
if(x == null)
return y;
return x+y;
};
strInt.put("abc", 0);
strInt.merge("abc", 1, bi);
strInt.merge("abc", 1, bi);
strInt.merge("abc", 1, bi);
strInt.merge("abcd", 1, bi);
System.out.println(strInt.get("abc"));
System.out.println(strInt.get("abcd"));
}
output is
3
1
Just for completeness in Java 8 there is a longAdder which brings some benefits in comparison to AtomicInteger (http://docs.oracle.com/javase/8/docs/api/java/util/concurrent/atomic/LongAdder.html)
final Map<WhatEver, LongAdder> result = new HashMap<>();
result.get(WhatEver).increment();
This should work
// If the key you want to add does not exist then add it as a new key
// And make the value 1
if (map.get(key) == null) {
map.put(key, 1);
} else {
// If the key does exist then replace the key's value with it's
// Original value plus one
map.put(key, map.get(key) + 1);
}
Found this to be the best way, avoiding NPE.
Map<Integer, Integer> map = new HashMap<>();
map.put(5, map.getOrDefault(5, 0) + 1);
System.out.println(map.get(5));
Output:
1

Categories