Get value from Hashmap by user object - java

I have such hashmap
HashMap<Man, Double> d = new HashMap<>();
Then I add new pair into it.
d.put(new Man("John"), 5.);
How can I retrieve this pair from this map?
I tried to do:
Man man = new Man("John");
System.out.println(d.get(man));
But as a result I have null while I expected 5.

This can only work if you override the methods equals(Object obj) and hashCode() in your class Man to allow your HashMap to understand that even if they are not the same instances, they have the same content and should be considered as the same key in your map.
So assuming that your class Man is something like:
public class Man {
private final String name;
public Man(final String name) {
this.name = name;
}
...
}
If you ask your IDE to generate the methods for you, you would get something like this:
#Override
public boolean equals(final Object o) {
if (this == o) return true;
if (o == null || this.getClass() != o.getClass()) return false;
final Man man = (Man) o;
return Objects.equals(this.name, man.name);
}
#Override
public int hashCode() {
return Objects.hash(this.name);
}

You need to have something unique that defines the object Man.
In your case, it appears to be -name.
So you override the equals() method and similarly the hashcode() methods in your Man class using name as the unique identifier.
Since name is a string you can delegate the task to similar methods in the String class.

The HashMap is working fine. Just because you make two new objects with the same name, doesn't mean the computer thinks they are the same object.
Try the following:
man1 = new Man("John");
man2 = new Man("John");
if (man1 == man2) {
System.out.println("Equal");
} else {
System.out.println("Not equal");
}
You'll get "not equal" because the computer is checking to see if they are exactly the same object, not just named the same.
Each time you're using the "new" keyword, you're declaring a new object and the computer gives it a unique address.
Try this:
man1 = new Man("John");
HashMap<Man, Double> myList = new HashMap<>();
myList.put(man1, "5.00");
System.out.print.ln(myList.get(man1));
you'll see you now get "5.00" back because you're actually giving it the exact object that is the key in the map.
You'll need to define manually how you decide that two "men" are equal if you want the behavior to work like this. You might be better off using a full name as a key, since that's usually going to be unique, and less work for you to implement.

Related

Hash map get method

Please explain output of below code. I guessed it null , because command line arguments are different than its key. But it is not correct explanation. It is null because friends class doesnot override equals and hashcode() methods.
But why?
import java.util.*;
public class Birthdays {
public static void main(String[] args) {
Map<Friends, String> hm = new HashMap<Friends, String>();
hm.put(new Friends("Charis"), "Summer 2009");
hm.put(new Friends("Draumur"), "Spring 2002");
Friends f = new Friends(args[0]);
System.out.println(hm.get(f));
}
}
class Friends {
String name;
Friends(String n) { name = n; }
}
And the command line invocation:
java Birthdays Draumur
args[0] will contain the string "Draumur", so that is not the reason for the program printing null.
A HashMap is a hash table, and it finds elements in it based on the hash value of the key. If you don't override the hash method, Java will calculate the hash value based on the object identity, so two different Friends objects, even with the same name inside, will not be guaranteed to hash to the same value.
You would also need to write an equals method, since if you don't override it, Java will also consider two different Friends objects not to be equal, even with the same name inside.
In summary, you need to override the hashCode method so the HashMap can find the Friends object, and you need to override the equals method so the HashMap, when it has found it, can see that it is the object it is searching for.
Here is a possible new version of the Friends class (and I would also suggest you call it Friend, since one such object represents one single friend):
class Friends {
String name;
Friends(String n) { name = n; }
public boolean equals(Object o) {
if (!(o instanceof Friends))
return false;
Friends rhs = (Friends)o;
return (name.equals(rhs.name));
}
public int hashCode() {
return name.hashCode();
}
}
command line arguments are different than its key
Not sure I understand that logic...
args = {"Draumur"}
new Friends(args[0]) = new Friends("Dramur")
A key like that was placed in the map already, but a Friend is not comparable to other Friend objects via a hashcode or equality otherwise.
If you had a HashMap of String to String, then get("Dramur") would not be null.
The get method of a map return the value of the key where map key ".equals" to researched key
Your Friends key do not implements equals, so the default one from Object is used, which is a "==" compare (true only if it is the same object).
Get will only give you something if you use the exact same object you put as key.

indexOf() for ArrayList of user defined objects not working

I am not getting the right answer when I try to use indexOf() of an ArrayList made up of user defined objects. Here is the code that creates one of the objects:
State kansas = new State("KS", 5570.81, 2000)
So, the name of the object is "kansas"
Here is the code that creates the ArrayList and adds the object:
ArrayList<State> allStates = new ArrayList<State>();
allStates.add(kansas);
And here is the code that I try to use to find the index of this object:
System.out.println(allStates.indexOf(kansas));
This is the point at which my compiler (Eclipse) throws me a red X indicating that there is a problem with my code and the problem is that it does not recognize 'kansas'. So I tried this:
String s = "kansas";
System.out.println(allStates.indexOf(s));
and it will run but the result is -1.
I am calling a method from a different class to create the ArrayList as opposed to creating it in the same class as my main method but I'm new enough to coding that I"m not sure if that is where I am going wrong. However, in order for the program that I am writing to work, I need to have data about each of the State objects stored so that I can access it from the main method.
Any advice?
*This is my first time posting a questions and I wasn't sure how much detail to go into so if I'm missing relevant information please let me know :)
method indexOf uses equlas() method to compare objects.
That why you have to override equals method in your custom class (if you planning use class in Map override hashCode method as well).
most IDE can generate these methods (equals and hashCode).
here simple example.
public class State {
private String stateCode;
public State(String stateCode /* other parameters*/) {
this.stateCode = stateCode;
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
State state = (State) o;
return stateCode.equals(state.stateCode);
}
#Override
public int hashCode() {
return stateCode.hashCode();
}
}
This is because, String is not your custom object State type. Your array list is a list of all 'State' types, which is why this -
String s = "kansas";
System.out.println(allStates.indexOf(s));
won't work.
What you can do is have a convenience method that iterates through the list and returns the index.
private int getIndexOfState(String stateName) {
for(State stateObject : allStates) {
if(stateObject.getName().equals(stateName))
return allStates.indexOf(stateObject);
}
return -1;
}
Now you can reuse this method to find index of any state name you pass, and whenever the method returns -1, it means the stateName(state) was not found in the list of states.You can pass in 'Kansas' or 'California' or anything as the parameter to the method.
In your method call you say
System.out.println(getIndexOfState("Kansas"));
System.out.println(getIndexOfState("Chicago"));
The return value is -1 because there is no String "kansas" in allStates, and ArrayList#indexOf returns -1 if the element is not present in the list. If you try to add s to allStates, the compiler won't even let you, because State is not a String.
I don't know why you instantiated a String with the value "kansas", but if you need to refer to the State from its name (maybe the name comes from a Scanner input), you will need a Map<String, State>, such as:
Map<String, State> map = new HashMap<>();
map.put("kansas", kansas) // a String and the object named kansas
Then, you can do:
System.out.println(allStates.indexOf(map.get("kansas")))
//or
String s = "kansas";
System.out.println(allStates.indexOf(map.get(s)))

Java HashSet contains Object

I made my own class with an overridden equals method which just checks, if the names (attributes in the class) are equal. Now I store some instances of that class in a HashSet so that there are no instances with the same names in the HashSet.
My Question: How is it possible to check if the HashSet contains such an object. .contains() wont work in that case, because it works with the .equals() method. I want to check if it is really the same object.
edit:
package testprogram;
import java.util.HashSet;
import java.util.Set;
public class Example {
private static final Set<Example> set = new HashSet<Example>();
private final String name;
private int example;
public Example(String name, int example) {
this.name = name;
this.example = example;
set.add(this);
}
public boolean isThisInList() {
return set.contains(this);
//will return true if this is just equal to any instance in the list
//but it should not
//it should return true if the object is really in the list
}
public boolean remove() {
return set.remove(this);
}
//Override equals and hashCode
}
Sorry, my english skills are not very well. Please feel free to ask again if you don't understand what I mean.
In your situation, the only way to tell if a particular instance of an object is contained in the HashSet, is to iterate the contents of the HashSet, and compare the object identities ( using the == operator instead of the equals() method).
Something like:
boolean isObjectInSet(Object object, Set<? extends Object> set) {
boolean result = false;
for(Object o : set) {
if(o == object) {
result = true;
break;
}
}
return result;
}
The way to check if objects are the same object is by comparing them with == to see that the object references are equal.
Kind Greetings,
Frank
You will have to override the hashCode method also.
try this..
Considering only one property 'name' of your Objects to maintain uniqueness.
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + (name == null ? 0 : name.hashCode());
return result;
}
#Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
User other = (User) obj;
if (name == null) {
if (other.name != null) {
return false;
}
} else if (!name.equals(other.name)) {
return false;
}
return true;
}
I made my own class with an overridden equals method which just checks, if the names (attributes in the class) are equal.
This breaks the contract of .equals, and you must never do it no matter how convenient it seems.
Instead, if you want to index and look up elements by a certain attribute such as the name, use a HashMap<Name, YourType> to find them. Alternatively, use a TreeSet and pass it a Comparator that compares the name only. You can then remove the incorrect equals method.
There are then three ways if you want to find objects by reference equality:
Your objects have no inherent or useful notion of equality.
Don't implement equals. Leave it to its default. You can then use a HashSet to look for reference equality, and a HashMap or TreeSet to index them by any specific attributes.
Your objects do have a useful, universal notion of equality, but you want to find equivalent instances efficiently anyways.
This is almost never the case. However, you can use e.g. an Apache IdentityMap.
You don't care about efficiency.
Use a for loop and == every element.
HashSet contains uses the equals method to determine if the object is contained - and duplicates are not kept within the HashSet.
Assuming your equals and hashcode are only using a name field...
HashSet<MyObject> objectSet = new HashSet<MyObject>();
MyObject name1Object = new MyObject("name1");
objectSet.add(new MyObject("name1"));
objectSet.add(name1Object);
objectSet.add(new MyObject("name2"));
//HashSet now contains 2 objects, name1Object and the new name2 object
//HashSets do not hold duplicate objects (name1Object and the new object with name1 would be considered duplicates)
objectSet.contains(new MyObject("name1")) // returns true
objectSet.contains(name1Object) // returns true
objectSet.contains(new MyObject("name2")) // returns true
objectSet.contains(new MyObject("name3")) // returns false
If you wanted to check if the object in the HashSet is the exact object you are comparing you would have to pull it out and compare it directly using ==
for (MyObject o : objectSet)
{
if (o == name1Object)
{
return true;
}
}
If you do this alot for specific objects it might be easier to use a HashMap so you don't have to iterate through the list to grab a specific named Object. May be worth looking into for you because then you could do something like this:
(objectMap.get("name") == myNameObject) // with a HashMap<String, MyNameObject> where "name" is the key string.

Java - HashMap and HashSet not backed by Object.hashCode()?

I'm trying to write a server, which tracks its clients by a uniquely-generated ID using a HashMap<ClientID,Client>. The idea is, if I'm an admin and I want to boot somebody off the server, I look up the appropriate ClientID (which is really just a String; only difference is the ClientID class does the work of ensuring that no two clients are ever assigned the same ID) for that client and then enter a command such as "kick 12" (if the ClientID of the person I wanted to kick happened to be 12).
I assumed this would work because I figured that a HashMap was probably backed by internal use of the hashCode() method inherited from Object, and I designed the ClientID class in a way that would support the necessary lookup operations, assuming that's true. But apparently, it's not true - two keys with the same hashcodes are evidently not considered to be the same key in a HashMap (or HashSet).
I've created a simple example using HashSet to illustrate what I want to do:
import java.lang.*;
import java.io.*;
import java.util.*;
class ClientID {
private String id;
public ClientID(String myId)
{
id = myId;
}
public static ClientID generateNew(Set<ClientID> existing)
{
ClientID res = new ClientID("");
Random rand = new Random();
do {
int p = rand.nextInt(10);
res.id += p;
} while (existing.contains(res));
return res;
}
public int hashCode()
{
return (id.hashCode());
}
public boolean equals(String otherID)
{
return (id == otherID);
}
public boolean equals(ClientID other)
{
return (id == other.id);
}
public String toString()
{
return id;
}
public static void main(String[] args) throws IOException {
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
HashSet<ClientID> mySet = new HashSet<ClientID>();
ClientID myId = ClientID.generateNew(mySet);
mySet.add(myId);
String input;
do {
System.out.println("List of IDs/hashcodes in the set: ");
for (ClientID x: mySet)
System.out.println("\t" + x.toString() + "\t" + x.hashCode());
System.out.print("\nEnter an ID to test if it's in the set: ");
input = in.readLine();
if (input == null)
break;
else if (input.length() == 0)
continue;
ClientID matchID = new ClientID(input);
if (mySet.contains(matchID))
System.out.println("Success! Set already contains that ID :)");
else {
System.out.println("Adding ID " + matchID.toString() + " (hashcode " + matchID.hashCode() + ") to the set");
mySet.add(matchID);
}
System.out.println("\n");
} while (!input.toUpperCase().equals("QUIT"));
}
}
Using this code, it is impossible (as far as I can tell) to produce the output
Success! Set already contains that ID :)
... Instead, it will just keep adding values to that set, even if the values are duplicates (that is, they are equal with the equals method AND they have the same hashcode). If I'm not communicating this well, run the code for yourself and I think you'll quickly see what I mean... This makes lookups impossible (and it also means the Client.generateNew method does NOT work at all as I intend it to); how do I get around this?
In Java, for a particular class to function as a key in a hash, it must implement two methods.
public int hashCode();
public boolean equals(Object o);
These methods must operate coherently: if one object equals another, those objects must produce the same hash.
Note the signature for equals(Object o). Your equals methods are overloading equals, but you must override equals(Object o).
Your overriden equals methods are also broken, as others have noted, because you are comparing String identity, not value. Instead of comparing via str1 == str2, use str1.equals(str2).
Make the following amendments to your code and things should start working properly.
public boolean equals(Object o){
return o instanceof ClientID ? this.equals((ClientID) o);
}
public boolean equals(String otherID) {
return id.equals(otherID);
}
public boolean equals(ClientID other) {
return id.equals(other.id);
}
HashSet (and HashMap) use the Object.hashCode method to determine which hash bucket the object should go into, but not whether that object is equal to another object also in that bucket. For that, they use Object.equals. In your case, you've tried to implement that method using reference equality of the String ID -- not "actual" equality, but String equality. You also created a new overload of equals, rather than overriding Object.equals.
You can search on SO for lots and lots of questions about why String can't be compared using ==, but the tl;dr version is that you need to override boolean equals(Object) (not an overloaded method of the same name, but that method exactly -- it has to take Object) and check that the incoming object is a ClientID whose String id equals (ont ==s) this ClientID's String id.
BTW all of those reading this post:
uou should all be careful of any Java Collection that fatches
it's children by hashcode, in the case that it's child type's
hashcode depends on it's mutable state.
an example:
HashSet<HashSet<?>> or HashSet<AbstaractSet<?>> or HashMap varient:
HashSet retrieves it's item by it's hashCode, but it's item type
is a HashSet, and hashSet.hashCode depends on it's items state.
code for that matter:
HashSet<HashSet<String>> coll = new HashSet<HashSet<String>>();
HashSet<String> set1 = new HashSet<String>();
set1.add("1");
coll.add(set1);
print(set1.hashCode); //---> will output X
set1.add("2");
print(set1.hashCode); //---> will output Y
coll.remove(set1) // WILL FAIL TO REMOVE (SILENTLY)
End Code
-reason being is HashSet remove method uses HashMap
and it identifies keys by hashCode, while AbstarctSet's hashCode
is dynamic and depends upon the mutable properties of itself.
hope that helps

Vector.indexOf won't work for my class

I'm building simple phonebook. Thus a have created a class "Person":
public class Person implements Comparable<Person> {
String Name;
String number;
public Person(String name,String Num) {
Name=name;
number=Num;
}
public String getNumber() {
return number;
}
public String getName() {
return Name;
}
#Override
public int compareTo(Person another) {
return Name.compareTo(another.getName());
}
#Override
public String toString() {
return Name;
}
#Override
public boolean equals(Object obj) {
if(!(obj instanceof Person) && !(obj instanceof String))
{
return false;
}
else
{
if(obj instanceof Person)
return Name.toLowerCase().equals(((Person)obj).getName().toLowerCase());
else
return Name.toLowerCase().equals(((String)obj).toLowerCase());
}
}
#Override
public int hashCode() {
return Name.hashCode();
} }
In some other part of the program i'm creating a Vector, populate it with "Person" objects but when i try to search a person BY NAME using vctPerson.indexOf("John") I always get -1 as result (not found). What's wrong with my code? I have implemented custom "equals" that should work with strings, and according to docs, "indexOf" is using "equals" to compare objects...
EDIT: I KNOW, I SHOULD SEARCH AFTER PHONE NUMBER, NOT NAME BUT IT's IRRELEVANT FOR THIS EXAMPLE
What Vector does in indexOf:
if (o.equals(elementData[i]))
where o would be "John". So you would have to override Sting.equals to do the right comparison (just kidding). Or you could use
vector.indexOf(new Person("John", null));
which will call your equals. Strictly speaking that will solve your question.
But in the long run you should not use Vector for that, because every indexOf call will iterate through the list - this is not very efficient.
A better way is a Map like HashMap where you can store key-value pairs. Lookup using the key is much cheaper than Vector.indexOf if here are a couple of entries.
Map<String, Person> map = new HashMap<String, Person>();
Person p = new Person("John", "1234");
map.put(p.getName().toLowerCase(), p);
// loopup
Person john = map.get("John".toLowerCase());
you called vctPerson.indexOf("John") . In this case, Vector call "John".equals( vctPerson.get( indexValue ) . As equals of String is called, String's equals compare "John" and Person object.
But as String's equals() does not return true when target object is not an instance of String, "John".equals( vctPerson.get( indexValue ) always return false. So result is always -1.
So, you can't use vctPerson.indexOf("John"). If you want to use vector, you need to traverse vector manually.
Your equals is broken: You objects may equal to a String (and that's what you're trying to exploit), but no String may ever equal to you object. Breaking symmetry of equals breaks everything.
Well, to be on the safe side you can always use
1) Map<String,Person> to make the relation between a person and his name
2) Make your own class that extends java.util.Vector and overrides its indexOf method
3) place a breakpoint in your equals method and see what's going on when indexOf gets called. Whatever's going on, though, it's better that you don't rely on the current implementation of indexOf that's specified in the JDK documentation since it may get changed upon the release of a next version of the JDK :)

Categories

Resources