This question already has answers here:
Iterating over Key Set vs Iterating over Entry Set
(2 answers)
FindBugs warning: Inefficient use of keySet iterator instead of entrySet iterator
(5 answers)
Closed 28 days ago.
I'm scanning my code through SonarQube and it shows this code smell "Iterate over the "entrySet" instead of "keySet"". I tried, but I can't figure it out.
Sample code:
public Set<Date> getAccountShiftDate(Map<String, Set<String>> shiftDatesMap, List<Groups> shiftSchedule) {
// set that has the account shift dates
Set<Date> accountShiftDatesTemplate = new Hashset<>();
// iterate on accounts
for (String accounts : shiftDatesMap.keySet()) {
//get group of an account
Optional <Groups> shiftOptional = shiftList
.stream()
.filter(g -> StringUtils.equalsIgnoreCase(accounts,g.getLongName()))
.findFirst();
// ...
Can someone give me a reference to understand this.
If you iterate over shiftDatesMap.keySet() and later on call in your loop shiftDatesMap.get(accounts) you execute an unnecessary get operation for each entry.
Try to read an understand the description of the metioned code smell from SonarQube.
Instead you should use shiftDatesMap.entrySet() which gives you an Entry, i.e. as pair of the key and the value. So if you later on in your loop want to access the value for your given accounts key, you must only access the value from your entry, which is cheaper than calling a get operation on the map for each value.
Before:
for (String accounts : shiftDatesMap.keySet()) {
Set<String> shiftDates = shiftDatesMap.get(accounts); // unnecessary operation
}
After:
for (Map.Entry<String, Set<String>> entry: shiftDatesMap.entrySet()) {
String accounts = entry.getKey();
Set<String> shiftDates = entry.getValue(); // access value for free without 'get' on map
}
Related
This question already has answers here:
Why do I get a ConcurrentModificationException?
(3 answers)
Iterating through a Collection, avoiding ConcurrentModificationException when removing objects in a loop
(31 answers)
Closed 4 years ago.
I currently have a problem with removing a key in a hashmap. I created a hashmap within a hashmap. I need to remove a key by the value inside the hashmap within the hashmap. So the hashmap looks like this:
HashMap<String, String> people_attributes = new HashMap<String, String>();
Map<String, HashMap<String, String>> people = new HashMap<String, HashMap<String, String>>();
If I try to remove a key, replace remove_name.add(p_name) with people.remove(p_name) I get java.util.ConcurrentModificationException.
Right now, I use an arraylist to add the keys that needs to be remove, then loop through the arraylist to remove the keys from the hashmap.
So far this is the solution:
for(String p_name : people.keySet()) { // search through people with attribute
for(String p_attributes : people.get(p_name).keySet()) { // search through attributes map
if(p_attributes.equals(g_att)) { // when current attributes equal to guess attribute
p_value = people.get(p_name).get(p_attributes);
if(!p_value.equals(g_value)) { // if current value doesn't equal to guess value
remove_name.add(p_name);
}
}
}
}
for(String r_name : remove_name) {
people.remove(r_name);
}
EDIT:
Problem: I know I can use iterator, like all the other questions ask in stackoverflow, but I don't know how to loop twice to get into people_attributes hashmap.
This question already has answers here:
What's the quickest way to remove an element from a Map by value in Java?
(12 answers)
Closed 4 years ago.
I have an assignment that requires me to has a Map that is configured...
Map<Integer,Event> eventList = new HashMap<>();
And I have to write a method that has the following header...
public String removeEvent(Event eventObj)
The idea is to pass it an Event object, check if the event already exist as a value in the Map, and if it does, remove it and return a String message as confirmation that it has been removed.
The issue I have is that it stipulates that I cannot iterate over the map for the solution.
I can use the containsValue() method, or my overidden equals() method, to check if the object already exists in the map, but I now have the issue where I am not sure how I can remove the key pair value that matches?
Any assistance would be good as I am quite new to Maps and often struggle moving between Key and Value.
If you cannot iterate1 the Map, you need another Map to represent the reverse mapping; e.g.
Map<Integer,Event> forwardMap = new HashMap<>();
Map<Event,Integer> reverseMap = new HashMap<>();
....
void remove (Event event) {
Integer key = reverseMap.get(event);
if (key != null) {
forwardMap.remove(key);
reverseMap.remove(event);
}
}
Obviously, all operations that modify the forward map must make the corresponding modification to the reverse map.
1 - I'm assuming that all forms of iteration are disallowed. That includes using Java 8+ streams, where the iteration is happening under the hood. If that is not what you mean, then update your Question to make it clear what is allowed and what it not allowed.
The simplest solution would be:
eventList.values().remove(eventObj);
However this uses iteration under the hood. You cannot solve this without iteration.
The solution is to write two-phase code:
Collect the keys that belong to that value
Remove those keys - given that they have still the same value
One might consider some sort of locking in case of multi-threaded access. For the sake of simplicity we put the burden of this to the caller of the method:
public String removeEvent(Event eventObj) {
// phase 1: collect the related keys
List<Integer> keys = eventList.entrySet().stream()
.filter(entry -> eventObj.equals(entry.getValue()))
.map(entry -> entry.getKey())
.collect(Collectors.toList());
// phase 2: remove those keys
for (Integer key : keys) {
// this version of reomve double-checks if that key still has that value
eventList.remove(key, eventObj);
}
// it would be good to know the criteria of failure - when should we return something else?
return "Success";
}
As you cannot iterate, you should call a function that will do that for you internally. Map.replaceAll() should work. This method will go through each entry and replace its value with what is returned by the BiFunction parameter. The first parameter of the function is the key, second is the value. It should return the new value.
Example:
map.replaceAll((key, value) -> {
if ("foo".equals(value)) return null;
return value;
}
This question already has answers here:
Java: Detect duplicates in ArrayList?
(17 answers)
Closed 6 years ago.
Would anyone know the most efficient way to look for duplicates in a String ArrayList and print out the duplicates?
For example I have an ArrayList containing the following:
ArrayList<String> carLIST = new ArrayList<String>();
carLIST = {"Car1", "Car2", "Car3", "Car1", "Car2", "Car2"};
Basically anything in the list more than once I'm looking to find the duplicates (which I think I've done below) and also return a System.out.println(); to show the following:
Car1 : count=2
Car2 : count=3
Map<String,Integer> repeatationMap = new HashMap<String,Integer>();
for(String str : carLIST) {
if (repeatationMap.containsKey(str) {
repeatationMap.put(str,repeatationMap.get(str) +1);
}
else {
epeatationMap.put(str, 1);
}
// if (repeatationMap.get(str) > 1) {
// System.out.println(repeatationMap.entrySet());
// }
}
The code commented out is what I thought it would be to print out the duplicates but I'm seriously wrong! Have no idea how to print out the duplicate cars in the list and show its count.
Once you've done populating the map, you could iterate it and print only the entries with keys greater than 1:
for (Map.Entry<String, Integer> e : repeatationMap.entrySet()) {
if (e.getValue() > 1) {
System.out.println (e.getKey());
}
}
Note, BTW, that Java 8 allows you to do the entire counting and reduction flow in a single statement in a relatively elegant fashion:
List<String> duplicateCars =
carLIST.stream()
.collect(Collectors.groupingBy(Function.identity(), Collectors.counting()))
.entrySet()
.stream()
.filter(e -> e.getValue() > 1)
.map(Map.Entry::getKey)
.collect(Collectors.toList());
The commented out section will print something unreadable object description. Instead use:
System.out.println(repeatationMap.get(str));
Also, to avoid printing the same string several time create a boolean set to track which strings have already been printed. In this way you avoid looping through the map again.
If you want to show number of duplicates, you will be forced to make a second loop anyways.
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
How do I iterate over each Entry in a Map?
I make the personal profiles database,and I use HashMap to collect profile.
private HashMap<String, Profile> database;
but I want to write profile data to text files
PrintWriter fileOut = new PrintWriter(new FileWriter(fileName));
fileOut.println(database.size());
for(int i = 0; i < database.size();i++){
Profile eachProfile = database.get(key);
}
But I don't know how to get list of key to looping
How can I get data from HashMap respectively with another ways?
You could use Map.entrySet() and extended for:
for (Map.Entry<String, Profile> e: database.entrySet())
{
String s = e.getKey();
Profile p = e.getValue();
}
Have a look at the Map documentation here: http://docs.oracle.com/javase/7/docs/api/java/util/Map.html
You want a list of all keys which is available as keySet(). The values() and entrySet() methods are related.
You can use map.keySet to get the set of keys.
You can use map.values to get the collection of values
I have set up a HashMap like so:
Map<String, ArrayList<String>> theAccused = new HashMap<String, ArrayList<String>>();
... and I populate this by storing for every name (key), a list of names (value). So:
ArrayList<String> saAccused = new ArrayList<String>();
// populate 'saAccused' ArrayList
...
// done populating
theAccused.put(sAccuser, saAccused);
So now, I want to look through all of the entries in the HashMap and see if (for each 'sAccuser'), the list 'saAccused' contains a certain name. This is my failed attempt so far:
Set<String> setAccusers = theAccused.keySet();
Iterator<String> iterAccusers = setAccusers.iterator();
iterAccusers.next();
ArrayList<String> saTheAccused;
// check if 'sAccuser' has been accused by anyone before
for (int i = 0; i < theAccused.size(); i++) {
saTheAccused = theAccused.get(iterAccusers);
if (saTheAccused.contains(sAccuser)) {
}
iterAccusers.next();
}
... however I'm not sure how the Set and Iterator classes work :/ The problem is that I don't have the "values"... the names... the 'sAccuser's... for the HashMap available.
In a nutshell, I want to iterate through the HashMap and check if a specific name is stored in any of the lists. So how can I do this? Let me know if you need me to go into further detail or clear up any confusion.
Thanks.
In a nutshell, I want to iterate through the HashMap and check if a specific name is stored in any of the lists. So how can I do this?
There's two ways of iterating through the map that might be of interest here. Firstly, you can iterate through all of the mappings (i.e. pairs of key-value relations) using the entrySet() method, which will let you know what the key is for each arraylist. Alternatively, if you don't need the key, you can simply get all of the lists in turn via the values() method. Using the first option might look something like this:
for (Map.Entry<String, ArrayList<String>> entry : theAccused.entrySet())
{
String sListName = entry.getKey();
ArrayList<String> saAccused = entry.getValue();
if (saAccused.contains(sAccuser))
{
// Fire your logic for when you find a match, which can
// depend on the list's key (name) as well
}
}
To answer the broader questions - the Set interface simply represents an (unordered) collection of non-duplicated values. As you can see by the linked Javadoc, there are methods available that you might expect for such an unordered collection. An Iterator is an object that traverses some data structure presenting each element in turn. Typical usage of an iterator would look something like the following:
Iterator<?> it = ...; // get the iterator somehow; often by calling iterator() on a Collection
while (it.hasNext())
{
Object obj = it.next();
// Do something with the obj
}
that is, check whether the iterator is nonexhausted (has more elements) then call the next() method to get that element. However, since the above pattern is so common, it can be elided with Java 5's foreach loop, sparing you from dealing with the iterator itself, as I took advantage of in my first example.
Something like this?
for (List<String> list : theAccused.values()) {
if (list.contains("somename")) {
// found somename
}
}
This should make it work:
saTheAccused = theAccused.get(iterAccused.next());
However, to make your code more readable, you can have either:
for (List<String> values : theAccused.values()) {
if (value.contains(sAcuser)) {
..
}
}
or, if you need the key:
for (String key : theAccused.keySet()) {
List<String> accused = theAccused.get(key);
if (accused.contains(sAccuser)) {
}
}
You need to use the value from Iterator.next() to index into the Map.
String key = iterAccusers.next();
saTheAccused = theAccused.get(key);
Currently you're getting values from the Map based on the iterator, not the values returned by the iterator.
It sounds like you need to do two things: first, find out if a given name is "accused", and second, find out who the accuser is. For that, you need to iterate over the Entry objects within your Map.
for (Entry<String, List<String>> entry : theAccused.entrySet()) {
if (entry.getValue().contains(accused)) {
return entry.getKey();
}
}
return null; // Or throw NullPointerException, or whatever.
In this loop, the Entry object holds a single key-value mapping. So entry.getValue() contains the list of accused, and entry.getKey() contains their accuser.
Make a method that does it:
private String findListWithKeyword(Map<String, ArrayList<String>> map, String keyword) {
Iterator<String> iterAccusers = map.keySet().iterator();
while(iterAccusers.hasNext()) {
String key = iterAccusers.next();
ArrayList<String> list = theAccused.get(key);
if (list.contains(keyword)) {
return key;
}
}
}
And when you call the method:
String key = findListWithKeyword(map, "foobar");
ArrayList<String> theCorrectList = map.get(key);