How to track and increment the duplicates count using hashmap - java

My program is a very simple one. All I need to do is track the duplicates and print the unique and the duplicate elements separately. I am using a hashmap for this. (dup is an arraylist containing all the elements)
Map<Employee, Integer> newMap = new HashMap();
int count = 0;
for (Employee element : dup) {
System.out.println("oooo" + element);
if (newMap.put((Employee) element, count) != null) {
newMap.put((Employee) element, newMap.get(element) + 1);
}
}
System.out.println("oooo" + newMap);
The o/p generated is:
Employee No :9 Employee Name :Swasti Employee MailId :swasti#gmail.com=0, Employee No :2 Employee Name :Shanthi Employee MailId :shanthi#gmail.com=0
The records with duplicates are incremented and the o/p is :
Employee Name :Shreya Employee MailId :shreya#gmail.com=1, Employee Name :Sujatha Employee MailId :suravich#gmail.com=1
The problem here is that the count is not incrementing more than one for records with more number of duplicates. I don't understand why. Any sort of help on this would be appreciated.
P.S: I tried doing this with an arraylist and a hashset and did get an output but using a map seemed like a better way.

This is because with this line:
if (newMap.put((Employee) element, count) != null) {
you put 0 again each time (since count is always 0 in your code). And on the next line, your newMap.get(element) will therefore be 0...
You need to grab the old value and check that it is not null:
Integer oldValue = newMap.put((Employee) element, 0);
if (oldValue != null)
newMap.put((Employee) element, oldValue + 1);
Note that your dup should be a List<Employee>, not a raw List. This would avoid all the casts you have to make currently.

if (newMap.containsKey(element)) {
newMap.put(element, newMap.get(element) + 1);
}
Also since you using generics you need to typecast.

if (newMap.get(element) != null) {
newMap.put(element, newMap.get(element) + 1);
} else {
newMap.put(element, 1);
}

Related

Optimisation of searching HashMap with list of values

I have a map in which values have references to lists of objects.
//key1.getElements() - produces the following
[Element N330955311 ({}), Element N330955300 ({}), Element N3638066598 ({})]
I would like to search the list of every key and find the occurrence of a given element (>= 2).
Currently my approach to this is every slow, I have a lot of data and I know execution time is relative but it takes 40seconds~.
My approach..
public String occurance>=2 (String id)
//Search for id
//Outer loop through Map
//get first map value and return elements
//inner loop iterating through key.getElements()
//if match with id..then iterate count
//return Strings with count == 2 else return null
The reason why this is so slow is because I have a lot of ids which I'm searching for - 8000~ and I have 3000~ keys in my map. So its > 8000*3000*8000 (given that every id/element exists in the key/valueSet map at least once)
Please help me with a more efficient way to make this search. I'm not too deep into practicing Java, so perhaps there's something obvious I'm missing.
Edited in real code after request:
public void findAdjacents() {
for (int i = 0; i < nodeList.size(); i++) {
count = 0;
inter = null;
container = findIntersections(nodeList.get(i));
if (container != null) {
intersections.add(container);
}
}
}
public String findIntersections(String id) {
Set<Map.Entry<String, Element>> entrySet = wayList.entrySet();
for (Map.Entry entry : entrySet) {
w1 = (Way) wayList.get(entry.getKey());
for (Node n : w1.getNodes()) {
container2 = String.valueOf(n);
if (container2.contains(id)) {
count++;
}
if (count == 2) {
inter = id;
count = 0;
}
}
}
if (inter != (null))
return inter;
else
return null;
}
Based on the pseudocode provided by you, there is no need to iterate all the keys in the Map. You can directly do a get(id) on the map. If the Map has it, you will get the list of elements on which you can iterate and get the element if its count is > 2. If the id is not there then null will be returned. So in that case you can optimize your code a bit.
Thanks

Read a map N keys at a time

I have a map (instaWords) which is filled with thousands of words. I need to to loop over it N item at a time. Here is my code. In this code I need to read instaWords in the chunks of e.g 500 words and execute "updateInstaPhrases" with those words. Any help?
private static Map<InstaWord, List<Integer>> instaWords = new HashMap<InstaWord, List<Integer>>();
// here a couple of words are added to instaWords
updateInstaPhrases(instaWords);
private static void updateInstaPhrases(Map<InstaWord, List<Integer>> wordMap)
throws SQLException, UnsupportedEncodingException {
for (Map.Entry<InstaWord, List<Integer>> entry : wordMap.entrySet()) {
InstaWord instaWord = entry.getKey();
List<Integer> profiles = entry.getValue();
pst.setBytes(1, instaWord.word.getBytes("UTF-8"));
pst.setBytes(2, instaWord.word.getBytes("UTF-8"));
pst.setBytes(3, (instaWord.lang == null) ?·
"".getBytes("UTF-8") :·
instaWord.lang.getBytes("UTF-8"));
String profilesList = "";
boolean first = true;
for (Integer p : profiles) {
profilesList += (first ? "" : ", ") + p;
first = false;
}
pst.setString(4, profilesList);
pst.addBatch();
}
System.out.println("Words batch executed");
pst.executeBatch();
con.commit();
}
What I need is to iterate through a hashmap 'in chunks' (e.g. 500 item each time)
You may keep a counter, initialize to 0 and increment for each item, while collecting the items as you see fit (like, say, ArrayList<Map.Entry<InstaWord, List<Integer>>>). If counter (after increment) equals 500, process the whole batch, reset counter to 0 and clear the collection.
Another option is to have the counter control the loop and declare explicitly the iterator you draw the Map.Entrys from. In this way it’s probably a bit clearer what is going on.

Performing Loop Unrolling while iterating the hashmap

I am looking at a possibility of unrolling the loop which is written to iterate the elements in a hash map.Below posted is the code.
for (final Object key : map.keySet())
{
if (input_map.containsKey(key))
{
System.out.println("Matching key: " + key);
if (map.get(key).equals(input_map.get(key)))
{
System.out.println("hii!done");
}
else
{
System.out.println(key);
final String values =
key.
toString().
substring(key.toString().lastIndexOf("\\") + 1);
System.out.println("input_map" +
input_map.get(key));
System.out.println("map" + map.get(key));
}
}
}
Explanation:
Currently, comparison in the loop is being done based on one element at a time i.e "key".I am looking at a possibility where i can retrieve the next successive keys in one single iteration i.e(key,key+1,key+2).
Any Suggestions would be highly helpful.
Use the KeySet or EntrySet iterator() method and while loop through with hasNext() and next(). You need to handle cases where it has not 3 repeating elements. Then you have the 3 keys and should be able to easily access the values in the Map.
Iterator<Integer> it = myMap.keySet().iterator();
while(it.hasNext())
{
int first = it.next();
int second = it.next();
int third = it.next();
}

Converting singly linked list to a map

I have been given an assignment to change to upgrade an existing one.
Figure out how to recode the qualifying exam problem using a Map for each terminal line, on the
assumption that the size of the problem is dominated by the number of input lines, not the 500
terminal lines
The program takes in a text file that has number, name. The number is the PC number and the name is the user who logged on. The program returns the user for each pc that logged on the most. Here is the existing code
public class LineUsageData {
SinglyLinkedList<Usage> singly = new SinglyLinkedList<Usage>();
//function to add a user to the linked list or to increment count by 1
public void addObservation(Usage usage){
for(int i = 0; i < singly.size(); ++i){
if(usage.getName().equals(singly.get(i).getName())){
singly.get(i).incrementCount(1);
return;
}
}
singly.add(usage);
}
//returns the user with the most connections to the PC
public String getMaxUsage(){
int tempHigh = 0;
int high = 0;
String userAndCount = "";
for(int i = 0; i < singly.size(); ++i){//goes through list and keeps highest
tempHigh = singly.get(i).getCount();
if(tempHigh > high){
high = tempHigh;
userAndCount = singly.get(i).getName() + " " + singly.get(i).getCount();
}
}
return userAndCount;
}
}
I am having trouble on the theoretical side. We can use a hashmap or a treemap. I am trying to think through how I would form a map that would hold the list of users for each pc? I can reuse the Usage object which will hold the name and the count of the user. I am not supposed to alter that object though
When checking if Usage is present in the list you perform a linear search each time (O(N)). If you replace your list with the Map<String,Usage>, you'll be able to search for name in sublinear time. TreeMap has O(log N) time for search and update, HashMap has amortized O(1)(constant) time.
So, the most effective data structure in this case is HashMap.
import java.util.*;
public class LineUsageData {
Map<String, Usage> map = new HashMap<String, Usage>();
//function to add a user to the map or to increment count by 1
public void addObservation(Usage usage) {
Usage existentUsage = map.get(usage.getName());
if (existentUsage == null) {
map.put(usage.getName(), usage);
} else {
existentUsage.incrementCount(1);
}
}
//returns the user with the most connections to the PC
public String getMaxUsage() {
Usage maxUsage = null;
for (Usage usage : map.values()) {
if (maxUsage == null || usage.getCount() > maxUsage.getCount()) {
maxUsage = usage;
}
}
return maxUsage == null ? null : maxUsage.getName() + " " + maxUsage.getCount();
}
// alternative version that uses Collections.max
public String getMaxUsageAlt() {
Usage maxUsage = map.isEmpty() ? null :
Collections.max(map.values(), new Comparator<Usage>() {
#Override
public int compare(Usage o1, Usage o2) {
return o1.getCount() - o2.getCount();
}
});
return maxUsage == null ? null : maxUsage.getName() + " " + maxUsage.getCount();
}
}
Map can also be iterated in the time proportional to it's size, so you can use the same procedure to find maximum element in it. I gave you two options, either manual approach, or usage of Collections.max utility method.
With simple words: You use a LinkedList (singly or doubly) when you have a list of items, and you usually plan to traverse them,
and a Map implementation when you have "Dictionary-like" entries, where a key corresponds to a value and you plan to access the value using the key.
In order to convert your SinglyLinkedList to a HashMap or TreeMap, you need find out which property of your item will be used as your key (it must be an element with unique values).
Assuming you are using the name property from your Usage class, you can do this
(a simple example):
//You could also use TreeMap, depending on your needs.
Map<String, Usage> usageMap = new HashMap<String, Usage>();
//Iterate through your SinglyLinkedList.
for(Usage usage : singly) {
//Add all items to the Map
usageMap.put(usage.getName(), usage);
}
//Access a value using its name as the key of the Map.
Usage accessedUsage = usageMap.get("AUsageName");
Also note that:
Map<string, Usage> usageMap = new HashMap<>();
Is valid, due to diamond inference.
I Solved this offline and didn't get a chance to see some of the answers which looked to be both very helpful. Sorry about that Nick and Aivean and thanks for the responses. Here is the code i ended up writing to get this to work.
public class LineUsageData {
Map<Integer, Usage> map = new HashMap<Integer, Usage>();
int hash = 0;
public void addObservation(Usage usage){
hash = usage.getName().hashCode();
System.out.println(hash);
while((map.get(hash)) != null){
if(map.get(hash).getName().equals(usage.name)){
map.get(hash).count++;
return;
}else{
hash++;
}
}
map.put(hash, usage);
}
public String getMaxUsage(){
String str = "";
int tempHigh = 0;
int high = 0;
//for loop
for(Integer key : map.keySet()){
tempHigh = map.get(key).getCount();
if(tempHigh > high){
high = tempHigh;
str = map.get(key).getName() + " " + map.get(key).getCount();
}
}
return str;
}
}

Retrieving specific values in Multimap

I'm using a Multimap that has two values per key. Below is the code I'm using to get each value separately:
The first bit of code gets the first object value:
for(Object object : map.get(object))
{
return object
}
Then, I'm using another method to retrieve the other value. This method takes the first object as an argument:
for(Object object : team.get(object))
{
if(object != initialObject)
{
return object;
}
}
This seems like a 'hackish' way of doing things, so is there any way for me to get the values more easily?
If you're using Guava, Iterables#get is probably what you want - it returns the Nth item from any iterable, example:
Multimap<String, String> myMultimap = ArrayListMultimap.create();
// and to return value from position:
return Iterables.get(myMultimap.get(key), position);
If you're using a ListMultimap then it behaves very much like a map to a List so you can directly call get(n).
Collection<Object> values = map.get(key);
checkState(values.size() == 2, String.format("Found %d values for key %s", values.size(), key));
return values.iterator().next(); // to get the first
Iterator<Object> it = values.iterator();
it.next(); // move the pointer to the second object
return it.next(); // get the second object
for getting all the values for the same key the most easiest way to do it will be :
Iterator iterator = multimap.get(key).iterator();
System.out.println("first element: " + iterator.next());
System.out.println("second element: " + iterator.next());
System.out.println("third element: " + iterator.next());
And so on
another option if you want to get all the elements without know how much elements there
Iterator iterator = multimap.get(key).iterator();
int index=1;
while (iterator.hasNext()) {
System.out.println(index +" element: " + iterator.next());
index++;
}

Categories