I want to remove all entries from a LinkedHashMap that were added after an entry with a given key.
My first try was:
LinkedHashMap<String, SomeObject> var = new LinkedHashMap<String, SomeObject>();
public void removeEntriesAfter(String key) {
boolean deleteEntries = false;
for (String currentKey : var.keySet()) {
if(deleteEntries) {
var.remove(currentKey);
} else {
if(key.equalsIgnoreCase(currentKey)) {
// Do not remove the current entry
deleteEntries = true;
}
}
}
}
But then I received a java.util.ConcurrentModificationException.
My second idea was to first determine the keys, an remove them afterwards.
public void removeEntriesAfter(String key) {
boolean deleteEntries = false;
List<String> listOfEntriesToBeRemoved = new ArrayList<String>();
// Determine entries to be deleted
for (String currentKey : var.keySet()) {
if(deleteEntries) {
listOfEntriesToBeRemoved.add(currentKey);
} else {
if(key.equalsIgnoreCase(currentKey)) {
// Do not remove the current entry
deleteEntries = true;
}
}
}
// Removed selected entries
for (String currentKey : listOfEntriesToBeRemoved) {
var.remove(currentKey);
}
}
That works, but I'm sure there is a more elegant/efficient way of doing this.
To avoid a ConcurrentModificationException you can use an Iterator.
Iterator<String> it = map.keySet().iterator();
while (it.hasNext())
if (it.next().equalsIgnoreCase(currentKey))
break;
while (it.hasNext()) {
it.next();
it.remove();
}
If you wanted the most efficient solution, it would be to go straight to the appropriate entry in the first place. To do this you would have to only ever put lower case keys into the map (rather than putting any old strings and comparing using equalsIgnoreCase). Then, using reflection, you could access the Map.Entry object corresponding to currentKey.toLowerCase() and then, using reflection again, you could follow the links all the way through the map. None of this is possible without reflection because neither the entry corresponding to a key nor the links between entries are exposed through public API. I do not recommend reflection as your code could easily break in the future if the code for LinkedHashMap is changed.
Related
I created a Map<Integer, ArrayList<String>> map and I would like to compare each value in map with one ArrayList<String> likeList and get key if they match. I will bring the key to use later.
I tried to run my code like this, but it doesn't work because it returns nothing:
for (int key : map.keySet()) {
if(map.get(key).equals(likeList)){
index = key;
Log.d("IndexN", String.valueOf(index));
}
}
Then, I tried this:
int index = 0;
for (Map.Entry<Integer, ArrayList<String>> entry : map.entrySet()) {
if(entry.getValue().equals(likeList)){
index = entry.getkey();
}
}
Do you have any idea?
Add a list of the key to store all match
List<Integer> indices = new ArrayList<>();
for (int key : map.keySet()) {
if (map.get(key).equals(likeList)) {
indices.add(key);
}
}
It does not return index when I try the code above.
From this comment, I understood that as soon as you find a match in the map, the index should be recorded and further processing should be stopped. In other words, either there is only one match of likeList in the map or you want to find the first match of likeList in the map. If yes, you need to break the loop as soon as the match is found (shown below).
for (int key : map.keySet()) {
if (map.get(key).equals(likeList)) {
Log.d("IndexN", String.valueOf(index));
break;
}
}
Note that this will give you the same value, each time you execute it, only when the map has only one match of likeList or the map is a LinkedHashMap. If it is a HashMap and it has more than one matches of likeList, you may get a different value each time you execute it because a HashMap does not guarantee the order of its entries.
However, if there can be multiple matches of likeList in the map and you want to log all the matches as well as get the list of the corresponding keys, you can do it as follows:
List<Integer> indexList = new ArrayList<>();
for (int key : map.keySet()) {
if (map.get(key).equals(likeList)) {
Log.d("IndexN", String.valueOf(index));
indexList.add(key);
}
}
// Display the list of corresponding keys
System.out.println(indexList);
Im trying to get an object out of a HashMap and call a method from this object.
But insted of getting the object, I get a normal java.lang.Object.
public void setExits(HashMap<Direction, Exit> e){
this.exits = e;
Iterator it = e.entrySet().iterator();
while (it.hasNext()) {
Map.Entry exits = (Map.Entry) it.next();
Exit r = exits.getValue(); //HERE I GET ERROR
}
}
You are declaring type constraints in the method signature but in the method body you are not taking any advantage of using the type constraints.
What you are doing is similar to you are using HashMap< Object, Object >. That is why compile error.
Correct Code:
public void setExits(HashMap<Direction, Exit> e){
this.exits = e;
Iterator<Map.Entry<Direction, Exit>> it = e.entrySet().iterator();
while (it.hasNext()) {
Map.Entry<Direction, Exit> entry = it.next();
Exit r = entry.getValue(); // OK
}
}
Change this line:
Iterator it = e.entrySet().iterator();
to:
Iterator<Entry<Direction, Exit>> it = e.entrySet().iterator();
Here is how I might iterate every value in a HashMap
HashMap<Directive, Exit> tempHashMap = new HashMap<>();
for(Directive directive:tempHashMap.keySet()){
Exit tempExit = tempHashMap.get(directive);
//do what you want with the exit
}
You are using a HashMap like a list. It's not a very effective list.
Instead do
Object value = map.get(key);
And it will skip the items that aren't under the key, very efficiently.
public void setExits(HashMap<Direction, Exit> exits, Direction direction){
this.exits = e.get(direction);
}
What you have missed is the generics for the Map.Entry.
It looks to me as though you are trying to loop over all the entries of the map, you might find a for loop easier.
for(Map.Entry<Direction, Exit> entry : e.entrySet()) {
Direction dir = entry.value();
//do stuff
}
Entry which needs to compare with the List and get the value from Map which is not there is the List.
for (Map.Entry<String, Object> entry : itemObj.entrySet())
{
System.out.println(entry.getKey());
for (ItemProcessVO processVO : itemDetails2){
if (entry.getKey().equalsIgnoreCase(processVO.getAccount())){
String account = processVO.getAccount();
lstAccVO.add(account);
}
}
}
This is the code i have used.I have Map of entry.getKey() has 6 Values while itemDetail2 has only 5 elements.I need to display only the missing account after comparing.
Simply add an else-statement to your if clause that stores that account in a local variable. Then after your for loops you can do whatever with that.
Hint: you can use loop over Map#keySet() instead of Map#entrySet() and bypass the entries that way.
In the provided example you compared the key with the account, simply use the else- statement to find the missingAccounts to iterate after this loop over them.
List<ItemProcessVO> missingAccounts= new ArrayList<>();
for (Map.Entry<String, Object> entry : itemObj.entrySet())
{
System.out.println(entry.getKey());
for (ItemProcessVO processVO : itemDetails2){
if (entry.getKey().equalsIgnoreCase(processVO.getAccount())){
String account = processVO.getAccount();
lstAccVO.add(account);
}else{
missingAccounts.add(account)
}
}
}
Below code should do the trick. It uses case insensitive comparison and prints remaining keys in the end, more explanation is in comments:
public static void main(String[] args) throws IOException {
Map<String, Object> itemObj = new HashMap<>(); //Your Map
List<ItemProcessVO> itemDetails2 = new ArrayList<>();// Your list
//First, get all the keys of the map
Set<String> keys = new HashSet<>(itemObj.keySet());
//Now, iterate through list and remove the matching items
for(ItemProcessVO processVO : itemDetails2){
String key = pop(keys, processVO.getAccount());
//If key is not null then remove it
if(null != key){
keys.remove(key);
}
}
//Now, iterate through remaining keys and print the values
for(String key : keys){
System.out.println("Missing value " + itemObj.get(key));
}
}
private static String pop(Set<String> set, String key){
if(null == set){
return null;
}else{
for(String element : set){
if(element.equalsIgnoreCase(key)){
return element;
}
}
}
}
So I have a hashmap
HashMap<String, ArrayList<String> gMap = new HashMap<String, ArrayList<String>();
And when someone creates a group, the group leader is added to the key of the hashmap and then all the users inside the group are added to the arraylist
gMap.get(groupLeader).add(user);
I'm trying to make it so that only group leaders are allowed to invite players, but if a player is not part of any group and invites another user then a group is automatically created and the player becomes the group leader.
So, normally I would just do this
for(ArrayList<String> list : gMap.values()){
if(list.contains(user)){
//do something since the player is not part of the list
}
}
But I cannot do that since there could be multiple arrayLists, so even though the user is not part of one arrayList it does not mean that they aren't inside another.
So I'm curios how I would check all the arrayLists and do something only if the user is not part of any of them.
You're actually creating a Multimap (a mapping of keys to collections of values) data structure here, and you'll find if you use one directly you won't have to reinvent the wheel as you are now. Guava defines a very nice Multimap interface, including an ArrayListMultimap that stores the data you want, and has a containsValue() method that cleanly does what you need. As mentioned by others, contains checks against a list are slow, you could use HashMultimap to do these contains checks more efficiently if you don't actually care about order, or LinkedHashMultimap if you really do.
And if you aren't already using Guava, you're missing out - it provides countless excellent utilities and good practices.
Use a boolean value that you only change the value of if a list contains the user - and break out of the loop if it does.
boolean notInAnyList = true;
for(ArrayList<String> list : gMap.values()){
if(list.contains(user)){
notInAnyList = false;
break; // No point in iterating any further
}
}
if (notInAnyList) {
// Handle user not being in any of the lists here
}
So here is a code sample
boolean userIsPartOfGroup = false
for(ArrayList<String> list : gMap.values()){
if(list.contains(user)){
userIsPartOfGroup = true;
break;
}
}
if(!userIsPartOfGroup){
gMap.add(user).add(new ArrayList(user));
}
First, I would use a set instead of a list (Java 7 syntax):
Map<String, Set<String> gMap = new HashMap<>();
When I understand your goal right, this may be a solution:
if (isLeader(groupLeader, gMap)) {
gMap.get(groupLeader).add(user);
} else if (isMember(groupLeader, gMap)) {
throw new UnsupportedOperationException("Member " + groupLeader + " is not a leader and must not invite " + user);
} else {
addNewGroup(groupLeader, gMap).add(user);
}
Here are the helper methods:
private boolean isLeader(String player, Map<String, Set<String> gMap) {
return gMap.keys().contains(player);
}
private boolean isMember(String player, Map<String, Set<String> gMap) {
for (Set<String> members : gMap.values()) {
if (members.contains(player)) {
return true;
}
}
return false;
}
private Set<String> addNewGroup(String leader, Map<String, Set<String> gMap) {
Set<String> players = new HashSet<>();
gmap.put(leader, players);
return players;
}
I have an object as Riziv with three variables as id, cnk and product. Then I search in a databank for this object and add it to a ArrayList as ArrayList<Riziv> list.
Now I should checkout if all object in his array are the same cnk then return true otherwise I should return all objects which are not the same cnk with error message.
public class Riziv{ String id, cnk, product; }
ArrayList<Riziv> list = getArrayListFromDatabank(id);
public void getDuplicatedWhichHasTheSameCnk(){
}
}
Using standard JVM structures (MultiMap is provided by guava), you can do that:
public List<Riviz> getDuplicates(final List<Riviz> l)
{
final HashMap<String, List<Riviz>> m = new HashMap<String, List<Riviz>>();
final List<Riviz> ret = new ArrayList<Riviz>();
String cnk;
for (final Riviz r: l) {
cnk = r.getCnk();
if (!m.contains(cnk))
m.add(cnk, new ArrayList<Riviz>());
m.get(cnk).add(r);
}
List<Riviz> tmp;
for (final Map.Entry<String, List<Riviz>> entry: m.entrySet()) {
tmp = entry.getValue();
if (tmp.size() == 1) // no dups
continue;
ret.addAll(tmp);
}
return ret;
}
ret will contain the duplicates. You can change that function to return a Map<String, Riviz> instead, and filter out entries where the list size is only one. You'll then get a map with the conflicting cnks as keys and a list of dups as values.
I am not clear exactly what you want however I suspect you want something like this.
MultiMap<Key, Riziv> multiMap =
List<Riziv> list =
for(Riziv r: list)
multiMap.put(r.getCnk(), r);
for(Key cnk: multiMap.keySet()) {
Collection<Riziv> sameCnk = multiMap.get(cnk);
// check size and compare entries
}
The multi-map will have the list of Riziv objects for each Cnk.
One way to do it is write a comparator to sort the list by cnk String and then compare each consecutive cnk String to the next, if you find a duplicate, they will be right next to eachother.
1.) Sort the list using a comparator by sorting on the cnk variable.
2.) Compare each element in the list to the next for duplicates.
There's probably many other ways to solve this, this is just the first that came to mind.
I did not test this so you have been forewarned lol:
ArrayList<Riziv> rizArray = new ArrayList<Riziv>();
//Sort the array by the CNK variable.
Collections.sort(rizArray, new Comparator<Riziv>(){
#Override
public int compare(Riziv arg0, Riziv arg1) {
//Return the comparison of the Strings.
//Use .compareToIgnoreCase if you want to ignore upper/lower case.
return arg0.getCnk().compareTo(arg1.getCnk());
}
});
//List should be in alphabetical order at this point.
List<Riziv> duplicates = new ArrayList<Riziv>();
Riziv rizPrevious = null;
for(Riziv riz: rizArray){
if(rizPrevious == null){
rizPrevious = riz;
continue;
}
if(riz.getCnk().compareTo(rizPrevious.getCnk()) == 0){
duplicates.add(riz);
}
rizPrevious = riz;
}