Null-value when iterating through Hash key set - java

It puzzles me how the following segment can lead to a null value of the Boolean mandatory, although it is not null at the corresponding key in the actual hashtable:
for (List<List<A>> a : hashMap.keySet()) {
Boolean mandatory = hashMap.get(a);
}

A HashMap will return null if the key specified is not bound to a value.
Issue is almost certainly that comparison op on a -- a List -- against keys is failing.
Let me guess: are you modifying these lists (the key object) after you have called a put? Did you remove all entries in one of the keys? Remember an empty list is equal to all empty ArrayLists. Further remember that List.equals() compares list content (one by one) to test equality.
package sof_6462281;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Demonstrate the fact that the Map uses key.equals(k) to
* test for key equality. Further demonstrate that it is a
* very bad idea to use mutable collections are keys to maps.
*/
public class ListAsKey {
public static void main(String[] args) {
Map<List<A>, Boolean> map = new HashMap<List<A>, Boolean>();
List<A> alist = new ArrayList<A>();
map.put(alist, true);
for (List<A> a : map.keySet()) {
Boolean b = map.get(a);
System.out.format("\t%s(ArrayList#%d) => %s\n",a, a.hashCode(), map.get(a));
}
// you changed your list after the put, didn't you?
alist.add(new A());
for (List<A> a : map.keySet()) {
Boolean b = map.get(a);
System.out.format("\t%s(ArrayList#%d) => %s\n",a, a.hashCode(), map.get(a));
}
alist.clear();
for (List<A> a : map.keySet()) {
Boolean b = map.get(a);
System.out.format("\t%s(ArrayList#%d) => %s\n",a, a.hashCode(), map.get(a));
}
}
public static final class A { /* foo */ }
}
Results:
[](ArrayList#1) => true
[sof_6462281.ListAsKey$A#4b71bbc9](ArrayList#1265744872) => null
[](ArrayList#1) => true
edit: added more ops to above and added console out.

Boolean can be null because it wraps the value-type primitive boolean. I am unsure what you mean its not null at the corresponding key in the actual hashtable. You are iterating over the keys then getting the values at those keys. The value at a key was inserted as null so when you are retrieving it you are getting the null.

Using a mutable object for a Map key is always a dangerous thing. If you maintain any reference to any of those keys after inserting into the map, then it is very likely that one of those keys will be modified at some point in the future which will invalidate the contents of your map.
A less likely, but possible scenario, even assuming you somehow don't screw up your List<List<>> key is if you have messed up the equals method of class A, then your Lists' equals method will also be messed up, again screwing up your map.
Look at alphazero's nice code example if you need further proof that what you are attempting to do is a bad idea.

Related

Writing a method that itterates over a Map, looks for Object entries that contain a value, adds this Object to a new set then returns that set

I am learning how to code in Java and struggling with this concept.
I need to write a method that takes a char argument which exists within the Objects that are in a map.
It needs to return a currently non existing Set of Objects that contain this char value as a new Set after iterating over the map to find these objects.
The following is my current code which doesn't get my desired outcome, I believe the problem is in the if statement. I have done lots of different combinations of equals() and containsValue on the object and map and cannot seem to get it to return true.
public Set findObj(char aChar)
{
Set<String> objSet = new HashSet();
for (Object Obj: map1.values())
{
if (map1.values().contains(aChar))
{
//if true add to objSet
}
}
return objSet;
}
If it helps the Map is
Map<String,Obj> map1= new HashMap<>();
and the Object is created by a class and contains a 3 variables, 2 string and one being the char value I am trying to find within each iteration of the map.
you are correct the problem is mainly in your if statement, since you're iterating over the objects in the map, you should check if the current object contains the string not whether or not the map as a whole contains it
public Set findObj(char aChar) {
Set<String> objSet = new HashSet();
for (Object c : map1.values()) {
if (c.toString().indexOf(aChar) >= 0) {
//if true add to objSet
}
}
return objSet;
}

How does one cycle through a HashMap <String, Object> and pass certain objects into a List based on their attributes for printing to the console?

The constraints of the project require me to use a HashMap as my source of data. I'm being asked to make the following method
getAccountsWithMinimum - returns a List of Accounts that have at least the specified balance
The problem is that HashMaps have no index. So it's not like I can use a For loop to go through it.
I tried adapting code from similar questions I found on Stack Overflow and GeeksForGeeks. This approach not only hasn't worked but doesn't much benefit me as I don't understand WHY it works even if it did (It doesn't).
I tried using this from code I found on GeeksForGeeks. It prints nothing.
public void getAccountsWithMinumum() {
Iterator entries = accounts.entrySet().iterator();
while (entries.hasNext()) {
Map.Entry entry = (Map.Entry) entries.next();
Integer key = (Integer) entry.getKey();
Integer value = (Integer) entry.getValue();
System.out.println("Key = " + key + ", Value = " + value);
}
}
The objects in accounts hold (String)firstName, (String)lastName, (double)balance, (String)accountType, and (String)AccountID attributes.
What I'd like to get is something that returns the desired list and prints it to the console so that I can confirm it.
I don't need someone to do this FOR me per say as I doubt this is the last time that I'll be asked to do this or something similar. Either some really heavy hand holding hints so I can fill in the gaps or if its easier just to do it a detailed explanation of WHY I'm doing it would be most appreciated.
With Java 8 streams:
List<Accout> accounts = accounts.values().stream()
.filter(account -> account.getBalance() > threshold)
.collect(Collectors.toList())
With foreach:
List<Account> accountsWithMinimum = new ArrayList<>();
for (Account acccount : accounts.values() ) {
if (account.balance > threshold) {
accountsWithMinimum.add(account);
}
}
The values method of the Map interface returns a Collection of the values stored in the map. You can also used entrySet to get the collection of key-value pairs, or keySet to get only the keys.
From information you provided best solution seems to be change data from HashMap to LinkedHashMap, that keep ordering.
If you take a close took to javadoc, you can find following part useful:
his implementation spares its clients from the unspecified, generally
chaotic ordering provided by HashMap (and Hashtable), without
incurring the increased cost associated with TreeMap. It can be used
to produce a copy of a map that has the same order as the original,
regardless of the original map's implementation:
void foo(Map m) {
Map copy = new LinkedHashMap(m);
...
}
This technique is particularly useful if a module takes a map on
input, copies it, and later returns results whose order is determined
by that of the copy. (Clients generally appreciate having things
returned in the same order they were presented.)
So the idea is to get in input the map, create a LinkedHashMap over it, iterate using forEach. Here an example with comments where you can start to elaborate on it
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map.Entry;
public class Main {
public static void main(String[] args) {
Main main = new Main();
main.test();
}
private void test() {
// your input
HashMap<Integer, Account> accounts = new HashMap<>();
accounts.put(0, new Account("first", 1));
accounts.put(1, new Account("second", 10));
accounts.put(2, new Account("third", 5));
// call your method
List<Account> result = getAccountsWithMinumum(accounts);
System.out.println(result.toString());
}
private List<Account> getAccountsWithMinumum(HashMap<Integer, Account> accounts) {
List<Account> result = new ArrayList<>();
// create linkedhashmap to get order
LinkedHashMap<Integer, Account> data = new LinkedHashMap<>(accounts);
// get entries
for (Entry<Integer, Account> entry : data.entrySet()) {
// print entry
System.out.println("key=" + entry.getKey() + ", value=" + entry.getValue());
// check account balanece on a value, for example 6 (use it as parameter!)
if (entry.getValue().balance < 6) {
// add to the result list
result.add(entry.getValue());
}
}
return result;
}
// your account class
private class Account {
public String name;
public int balance;
public Account(String name, int balance) {
this.name = name;
this.balance = balance;
}
#Override
public String toString() {
return "[name=" + name + ", balance=" + balance + "]";
}
}
}
Your answer is a little bit difficult to answer because of lack of what Hashmap is exactly, in my case is
The example you found predates the use of generics in Java. Any Map is these days actually a Map<K,V>, where K is the type (= the class) of the keys in the mapping, and V is the type (once again the java class) of the value objects the key values are mapped to. So the Map in your example (looking at what types the objects are cast to) would these days be written as Map<Integer,Integer>.
There are some distinct ways of iterating over the contents of a Map : iterating its entrySet(), iterating its keySet() or iterating its values().
The first results in objects of type Map.Entry (as in the example) from which you can subsequently retrieve both the key object and the value object.
The second results in objects of type K (type of the key) and will obviously give you only the key objects leaving you no access to the corresponding value object (except by using map.get(key) which is rather stupid given that you're already iterating and could have had the value object "for free").
The third results in objects of type V (type/class of the value objects), without access to the corresponding key, except if the map has been built using key objects from within the value objects.
Assuming your Account objects are the value objects, I'd guess you'd want to iterate over the values() and then check each returned object's getBalance().
Or you can use the more modern lambda-style syntax using streams, as has been sugested in other answers.
Was this the kind of "explanation" you were looking for ?

How to return keys of a map according to a condition?

I'm learning Java using BlueJ, I have made a class that has a HashMap of (Integer, String) that contains an ID number of somebody and their name.
I want a method to return a collection of all the keys that satisfy a condition, like if their ID number begins with 3 for example. I can't figure out how to do this.
And then another method that returns a collection of the values if they satisfy a condition, I was thinking it would be very similar to the previous method.
I know I need to loop through the map but I am not sure how to write the condition to populate the new map.
Here's an example that returns all the odd keys, in a Collection. Lists and Sets are Collections, in the same way that ArrayLists are Lists. You could change Collection to List (or even ArrayList) in this example and it would do the same thing.
public Collection<Integer> getOddKeys() {
// keySet is a method of Map that returns a Set containing all the keys (and no values).
Collection<Integer> result = new ArrayList<Integer>();
for(Integer key : map.keySet()) {
if((key % 2) == 0) // if the key is odd...
result.add(key); // ... then add it to the result
}
return result;
}
You should be able to modify this example to check the values instead - I won't just give you that code, because it's very similar, and easy to figure out if you understand how this example works.
You need to use the values method, which returns a collection of the values, in the same way that keySet returns a collection (more specifically, a set) of the keys. If you're wondering about why keySet returns a set and values doesn't, it's because you can use the same value twice in a map, but you can't use the same key twice.
You could do the following:
Create a holder list
Iterator over your map keys using map.keySet().iterator();
Check if the key start with 3, if yes add it to the key list.
return the keys list.
In your case (if the map is not too big), I'll get all keys of the map, then process them one by one to math my criteria:
Map<Integer, String> myMap=getFromSomeWhere();
for(Integer i : myMap.keySet() {
String k=String.valueOf(i);
if(k.startsWith("3")) {
//do what you want
}
}
public void CountryAbbriviationMap(String input)
{
map<string ,string> countrymap =new map<string ,string>{'Australia'=>'AUS','Argentina'=>'ARG', 'India'=>'IND'};
for(string key : countrymap.keySet())
{
if(key.startsWithIgnoreCase('A') && input.startsWithIgnoreCase('A'))
{
system.debug(key); //TO GET KEYS
system.debug(countrymap.get(key)); //TO GET VALUES
}
}
}

Java - HashMap confusion about collision handling and the get() method

I'm using a HashMap and I haven't been able to get a straight answer on how the get() method works in the case of collisions.
Let's say n > 1 objects get placed in the same key. Are they stored in a LinkedList? Are they overwritten so that only the last object placed in that key exists there anymore? Are they using some other collision method?
If they are placed in a LinkedList, is there a way to retrieve that entire list? If not, is there some other built in map for Java in which I can do this?
For my purposes, separate chaining would be ideal, as if there are collisions, I need to be able to look through the list and get information about all the objects in it. What would be the best way to do this in Java?
Thanks for all your help!
The documentation for Hashmap.put() clearly states, "Associates the specified value with the specified key in this map. If the map previously contained a mapping for the key, the old value is replaced"
If you would like to have a list of objects associated with a key, then store a list as the value.
Note that 'collision' generally refers to the internal working of the HashMap, where two keys have the same hash value, not the use of the same key for two different values.
Are they overwritten so that only the last object placed in that key exists there anymore?
Yes, assuming you're putting multiple values with the same key (according to Object.equals, not Object.hashCode.) That's specified in the Map.put javadoc:
If the map previously contained a mapping for the key, the old value is replaced by the specified value.
If you want to map a key to multiple values, you're probably better off using something like Guava's ListMultimap, ArrayListMultimap in specific, which maps keys to lists of values. (Disclosure: I contribute to Guava.) If you can't tolerate a third-party library, then really you have to have a Map<Key, List<Value>>, though that can get a bit unwieldy.
Let's say n > 1 objects get placed in the same key. Are they stored in a linked list? Are they overwritten so that only the last object placed in that key exists there anymore? Are they using some other collision method?
There could be single instance for the same key so the last one overrides the prior one
Map<String, Integer> map = new HashMap<String, Integer>();
map.put("a", 1);
map.put("a", 2);// it overrides 1 and puts 2 there
chaining comes where there turns the same hash for different keys
See
Java papers hash table working
Cite: "Let's say n > 1 objects get placed in the same key. Are they stored in a linked list? Are they overwritten so that only the last object placed in that key exists there anymore? Are they using some other collision method?"
Yes, if the hashmap contained something under this key, it will override it.
You can implement your own class to handle that or more simple use a HashMap> in where K is your Key Object and V the object value.
Have in mind that with last solution when you do a map.get(K) will retrieve a List or the implementation that you choose (i.e: ArrayList) so all the methods of this implementation are available for you and perhaps fulfils your requirements. For example if you used Arraylist you have the size, trimToSize, removeRange, etc.
collision resolution for hashing in java is not based on chaining. To my understanding, JDK uses double hashing which is one of the best way of open addressing. So there's no list going to be associated with a hash slot.
You might put the objects for which the hash function resolves to the same key can be put in list and this list can be updated in the table/map.
package hashing;
import java.util.HashMap;
import java.util.Map;
public class MainAnimal {
/**
* #param args
*/
public static void main(String[] args) {
Animal a1 = new Animal(1);
Animal a2 = new Animal(2);
Map<Animal, String> animalsMap = new HashMap<Animal, String>();
animalsMap.put(a1,"1");
animalsMap.put(a2,"2");
System.out.println(animalsMap.get(a1));
Map<String, Integer> map = new HashMap<String, Integer>();
map.put("a", 1);
map.put("a", 2);// it overrides 1 and puts 2 there
System.out.println(map.get("a"));
}
}
class Animal {
private int index = 0;
Animal(int index){
this.index = index;
}
public boolean equals(Object obj){
if(obj instanceof Animal) {
Animal animal = (Animal) obj;
if(animal.getIndex()==this.getIndex())
return true;
else
return false;
}
return false;
}
public int hashCode() {
return 0;
}
public int getIndex() {
return index;
}
public void setIndex(int index) {
this.index = index;
}
}
In the above code, am showing two different things.
case 1 - two different instances resolving to same hashkey
case 2 - two same instances acting as keys for two different entries.
Animal instances, a1 & a2 resolves to same key. But they are not overriden. Hashing mechanism probes through the hash slots and places the entries on different slots.
with the second case, keys resolve to same hash key and also the equals method satisfies. Hence overriding happens.
Now if in the animal class I override the equals method this way -
public boolean equals(Object obj){
// if(obj instanceof Animal) {
// Animal animal = (Animal) obj;
// if(animal.getIndex()==this.getIndex())
// return true;
// else
// return false;
// }
// return false;
return true;
}
Overriding happens. The behavior is like using same instance. Since a1 and a2 are in the same bucket and equals return true as well.

Collection removeAll ignoring case?

Ok so here is my issue. I have to HashSet's, I use the removeAll method to delete values that exist in one set from the other.
Prior to calling the method, I obviously add the values to the Sets. I call .toUpperCase() on each String before adding because the values are of different cases in both lists. There is no rhyme or reason to the case.
Once I call removeAll, I need to have the original cases back for the values that are left in the Set. Is there an efficient way of doing this without running through the original list and using CompareToIgnoreCase?
Example:
List1:
"BOB"
"Joe"
"john"
"MARK"
"dave"
"Bill"
List2:
"JOE"
"MARK"
"DAVE"
After this, create a separate HashSet for each List using toUpperCase() on Strings. Then call removeAll.
Set1.removeAll(set2);
Set1:
"BOB"
"JOHN"
"BILL"
I need to get the list to look like this again:
"BOB"
"john"
"Bill"
Any ideas would be much appreciated. I know it is poor, there should be a standard for the original list but that is not for me to decide.
In my original answer, I unthinkingly suggested using a Comparator, but this causes the TreeSet to violate the equals contract and is a bug waiting to happen:
// Don't do this:
Set<String> setA = new TreeSet<String>(String.CASE_INSENSITIVE_ORDER);
setA.add("hello");
setA.add("Hello");
System.out.println(setA);
Set<String> setB = new HashSet<String>();
setB.add("HELLO");
// Bad code; violates symmetry requirement
System.out.println(setB.equals(setA) == setA.equals(setB));
It is better to use a dedicated type:
public final class CaselessString {
private final String string;
private final String normalized;
private CaselessString(String string, Locale locale) {
this.string = string;
normalized = string.toUpperCase(locale);
}
#Override public String toString() { return string; }
#Override public int hashCode() { return normalized.hashCode(); }
#Override public boolean equals(Object obj) {
if (obj instanceof CaselessString) {
return ((CaselessString) obj).normalized.equals(normalized);
}
return false;
}
public static CaselessString as(String s, Locale locale) {
return new CaselessString(s, locale);
}
public static CaselessString as(String s) {
return as(s, Locale.ENGLISH);
}
// TODO: probably best to implement CharSequence for convenience
}
This code is less likely to cause bugs:
Set<CaselessString> set1 = new HashSet<CaselessString>();
set1.add(CaselessString.as("Hello"));
set1.add(CaselessString.as("HELLO"));
Set<CaselessString> set2 = new HashSet<CaselessString>();
set2.add(CaselessString.as("hello"));
System.out.println("1: " + set1);
System.out.println("2: " + set2);
System.out.println("equals: " + set1.equals(set2));
This is, unfortunately, more verbose.
It could be done by:
Moving the content of your lists into case-insensitive TreeSets,
then removing all common Strings case-insensitively thanks TreeSet#removeAll(Collection<?> c)
and finally relying on the fact that ArrayList#retainAll(Collection<?> c) will iterate over the elements of the list and for each element it will call contains(Object o) on the provided collection to know whether the value should be kept or not and here as the collection is case-insensitive, we will keep only the Strings that match case-insensitively with what we have in the provided TreeSet instance.
The corresponding code:
List<String> list1 = new ArrayList<>(
Arrays.asList("BOB", "Joe", "john", "MARK", "dave", "Bill")
);
List<String> list2 = Arrays.asList("JOE", "MARK", "DAVE");
// Add all values of list1 in a case insensitive collection
Set<String> set1 = new TreeSet<>(String.CASE_INSENSITIVE_ORDER);
set1.addAll(list1);
// Add all values of list2 in a case insensitive collection
Set<String> set2 = new TreeSet<>(String.CASE_INSENSITIVE_ORDER);
set2.addAll(list2);
// Remove all common Strings ignoring case
set1.removeAll(set2);
// Keep in list1 only the remaining Strings ignoring case
list1.retainAll(set1);
for (String s : list1) {
System.out.println(s);
}
Output:
BOB
john
Bill
NB 1: It is important to have the content of the second list into a TreeSet especially if we don't know the size of it because the behavior of TreeSet#removeAll(Collection<?> c) depends on the size of both collections, if the size of the current collection is strictly bigger than the size of the provided collection, then it will call directly remove(Object o) on the current collection to remove each element, in this case the provided collection could be a list. But if it is the opposite, it will call contains(Object o) on the provided collection to know whether a given element should be removed or not so if it is not an case-insensitive collection, we won't get the expected result.
NB 2: The behavior of the method ArrayList#retainAll(Collection<?> c) described above is the same as the behavior of the default implementation of the method retainAll(Collection<?> c) that we can find in AbstractCollection such that this approach will actually work with any collections whose implementation of retainAll(Collection<?> c) has the same behavior.
You can use a hashmap and use the capital set as keys that map to the mixed case set.
Keys of hashmaps are unique and you can get a set of them using HashMap.keyset();
to retrieve the original case, it's as simple as HashMap.get("UPPERCASENAME").
And according to the documentation:
Returns a set view of the keys
contained in this map. The set is
backed by the map, so changes to the
map are reflected in the set, and
vice-versa. The set supports element
removal, which removes the
corresponding mapping from this map,
via the Iterator.remove, Set.remove,
removeAll, retainAll, and clear
operations. It does not support the
add or addAll operations.
So HashMap.keyset().removeAll will effect the hashmap :)
EDIT: use McDowell's solution. I overlooked the fact that you didn't actually need the letters to be upper case :P
This would be an interesting one to solve using google-collections. You could have a constant Predicate like so:
private static final Function<String, String> TO_UPPER = new Function<String, String>() {
public String apply(String input) {
return input.toUpperCase();
}
and then what you're after could be done someting like this:
Collection<String> toRemove = Collections2.transform(list2, TO_UPPER);
Set<String> kept = Sets.filter(list1, new Predicate<String>() {
public boolean apply(String input) {
return !toRemove.contains(input.toUpperCase());
}
}
That is:
Build an upper-case-only version of the 'to discard' list
Apply a filter to the original list, retaining only those items whose uppercased value is not in the upper-case-only list.
Note that the output of Collections2.transform isn't an efficient Set implementation, so if you're dealing with a lot of data and the cost of probing that list will hurt you, you can instead use
Set<String> toRemove = Sets.newHashSet(Collections2.transform(list2, TO_UPPER));
which will restore an efficient lookup, returning the filtering to O(n) instead of O(n^2).
as far as i know, hashset's use the object's hashCode-method to distinct them from each other.
you should therefore override this method in your object in order to distinct cases.
if you're really using string, you cannot override this method as you cannot extend the String-class.
therefore you need to create your own class containing a string as attribute which you fill with your content. you might want to have a getValue() and setValue(String) method in order to modify the string.
then you can add your own class to the hashmap.
this should solve your problem.
regards

Categories