I really don't understand from the below code how the get() methods, set() methods and toString() are called. Could someone explain me ?
The whole point here is after seeing the output I don't understand how the tostring method has been called. I don't see anything explicitly being called.
public class MyDuplicateKeyEx {
public static void main(String a[]){
HashMap<Price, String> hm = new HashMap<Price, String>();
hm.put(new Price("Banana", 20), "Banana");
hm.put(new Price("Orange", 30), "Orange");
printMap(hm);
Price key = new Price("Banana", 20);
System.out.println("Adding duplicate key...");
hm.put(key, "Grape");
System.out.println("After adding dulicate key:");
printMap(hm);
}
public static void printMap(HashMap<Price, String> map){
Set<Price> keys = map.keySet();
for(Price p:keys){
System.out.println(p+"==>"+map.get(p));
}
}
}
class Price{
private String item;
private int price;
public Price(String itm, int pr){
this.item = itm;
this.price = pr;
}
public int hashCode(){
int hashcode = 0;
hashcode = price*20;
hashcode += item.hashCode();
return hashcode;
}
public boolean equals(Object obj){
if (obj instanceof Price) {
Price pp = (Price) obj;
return (pp.item.equals(this.item) && pp.price == this.price);
} else {
return false;
}
}
public String getItem() {
return item;
}
public void setItem(String item) {
this.item = item;
}
public int getPrice() {
return price;
}
public void setPrice(int price) {
this.price = price;
}
public String toString(){
return "item: "+item+" price: "+price;
}
}
Output:
item: Apple price: 40==>Apple
item: Orange price: 30==>Orange
item: Banana price: 20==>Banana
Adding duplicate key...
After adding dulicate key:
item: Apple price: 40==>Apple
item: Orange price: 30==>Orange
item: Banana price: 20==>Grape
Thanks !!
You are right, there is no explicit call to toString. But under the hood, that is what Java is doing. When seeing p+"==>"+map.get(p), Java is doing p.toString()+"==>"+map.get(p).toString(). That is why you can concatenate strings and objects without problems.
Additionally, a better way of iterating through the key/values of a Map is:
public static void printMap(HashMap<Price, String> map) {
for (Map.Entry<Price, String> entry : map.entrySet()) {
System.out.println(entry.getKey() + "==>" + entry.getValue())
}
}
When using a HashMap with user-defined objects as keys, you must be very careful that you do never modify the fields used to compute the hashCode if they are present in the map. This is why you'll often see that final fields should be used to compute it. With a large program, this avoids lots of unnecessary mistakes.
Of course there is no explicit toString! Java does this implicitly for you! You should thank him, ya know.
The magic lies behind this code:
p+"==>"+map.get(p)
Here, p is a Price and map.get(p) is a String. So the above is basically adding a price to a string, then add another string to the result.
When Java wants to add any object to a string, it calls that object's toString to convert that object to String first. Otherwise, how can a price be added to a string, right?
Tip:
Never use objects which hash code can change as keys of a hash map. I have actually seen a person who was very extreme and he implemented the hashCode method with Math.random()! Here's the post: Could not understand the output And asked why can a hash map store things with the same hash code.
So to avoid that confusion, please don't use mutable objects as keys. Just remove those setters and you'll be fine.
Related
I need to have a Sorted Map where all elements are sorted by some key, but I need to be able to get elements by another key.
I assumed that I can solve this task by creating a custom key:
public class MyKey implements Comparable<MyKey>{
private long id;
private double price;
public MyKey(long orderId, double price) {
this.id = id;
this.price = price;
}
#Override
public int hashCode(){
return Objects.hash(id);
}
#Override
public boolean equals(Object o){
if(!(o instanceof MyKey)) return false;
return id == ((MyKey) o).id;
}
#Override
public int compareTo(MyKey o) {
if(price > o.price) return 1;
if(price < o.price) return -1;
return 0;
}
}
Here is I need to be able to get elements by key, but I need to force Map be sorted by price.
I tried to use:
Map<MyKey, Integer> myTestMap = new ConcurrentSkipListMap<>();
myTestMap.put(new MyKey(1, 200.0), 1);
myTestMap.put(new MyKey(2, 100.0), 2);
myTestMap.put(new MyKey(3, 300.0), 3);
myTestMap.put(new MyKey(6, 500.0), 6);
myTestMap.put(new MyKey(5, 400.0), 5);
myTestMap.put(new MyKey(4, 600.0), 4);
In this case Map is successfully sorted by price, but I can't get the element by using:
System.out.println(myTestMap.get(new MyKey(2, 0)));
I have to set price also to be able to get the element:
System.out.println(myTestMap.get(new MyKey(2, 100.0)));
Are there any workaround in this case?
No real solution to your answer, but an explanation why it is not working: If your dig into ConcurrentSkipListMap you'll see that getting an object checks an internal Index-structure and uses the compareTo method of your Comparable. It is not just using the hashCode method that takes only id into account (for which it would seem plausible to give just your id into the adhoc created MyKey instance).
I'd too suggest using a different approach.
I have a shopping site, in which items can be added to a basket, each item is an instance of a class Product, and all the items are stored in a Collection<Product> items,
I am currently then iterating through that list and displaying each item in a table.
However I want to display a quantity value for each item instead.
I created a Map, and am trying to put each of my products into it.
However each Product is still listed as only existing once because each class instance is different?
How would I adjust this?
My Product class has a product ID value. Here's the code I have currently.
Map<Product, Integer> map = new HashMap<>();
for (Product p : items) {
Integer i = map.get(p);
if (i == null) {
map.put(p, 1);
}
else {
map.put(p, i+1);
}
}
Having implemented hashcode and equals methods.
Trying to add the items to the map.
Collection<Product> items = basket.getItems();
Map<Product, Integer> map = new HashMap<>();
for (Product p : items) {
for (Product key : map.keySet()) {
if (p.equals(key)) {
map.put(key, map.get(key));
}
else {
map.put(p, 1);
}
}
}
However each Product is still listed as only existing once because each class instance is different?
Yes.
HashMap identifies keys by using their implementation of hashcode() and equals().
You you either use a property, which already has a proper implementation of both (as #zsmb13 suggested) or you create implementations of hashcode() and equals() in your Product class (ATTENTION! do not inherit them! They must be implemented in a decent child which will not be extended itself...).
You need to override the equals and the hashCode of Product Class for your hashing based operations to function properly. You need your Product class something like this
class Product{
private int price;
private String name;
public Product(String itm, int pr){
this.name = itm;
this.price = pr;
}
public int hashCode(){
int hashcode = 0;
hashcode = price*20;
hashcode += name.hashCode();
return hashcode;
}
public boolean equals(Object obj){
if (obj instanceof Product) {
Product pp = (Product) obj;
return (pp.name.equals(this.name) && pp.price == this.price);
} else {
return false;
}
}
}
I am trying to make kind of highscores in Java.
Basically I want a hashmap to hold the double value (so index starts from the highest double, so it's easier for me to sort highscores) and then the second value will be the client object, like this:
private HashMap<Double, TempClient> players = new HashMap<Double, TempClient>();
And to insert a new value:
TempClient client = new TempClient(kills, rank, deaths, name);
this.players.put(client.getKdr(), client);
Now, of course I can't iterate through the hashmap because it gets the list item by key, not index.
How can I iterate through a hashmap? or any good ideas for my case?
I tried it in a Foo class:
Output:
0.5
0.6
0.9
0.1
2.5
Code:
public class Foo {
public static void main(String[] args) {
HashMap<Double, String> map = new LinkedHashMap<Double, String>();
map.put(0.5, "hey");
map.put(0.6, "hey1");
map.put(0.9, "hey2");
map.put(0.1, "hey425");
map.put(2.5, "hey36");
for (Double lol : map.keySet()) {
System.out.println(lol);
}
}
}
You can iterate like this.
for (Double k : players.keySet())
{
TempClient p = players.get(k);
// do work with k and p
}
If you want to keep keys sorted, use e.g. a TreeMap.
If you want to keep the keys in the order you inserted
them in there, use e.g. a LinkedHashMap.
The best way is to iterate through hashmap is using EntrySet.
for (Map.Entry<Double, TempClient> entry : map.entrySet()) {
Double key= entry.getKey();
TempClient value= entry.getValue();
// ...
}
You'd be better off making your TempClient objects implement Comparable, adding them to a list, and then just using Collections.sort().
Since you can't sort items in a HashMap, nor you can sort them by value in a TreeMap you could use a TreeSet with a custom class:
class Score implements Comparable<Score>
{
final Player player;
final int score;
Score(Player player, int score) {
this.player = player;
this.score = score;
}
public int compareTo(Score other) {
return Integer.compare(this.score, other.score);
}
public int hashCode() { return player.hashCode(); }
public boolean equals(Object o) { return this.player.equals(...); }
}
TreeSet<Score> scores = new TreeSet<Score>();
score.add(new Score(player, 500));
for (Score s : scores) {
..
}
This will have both the advantages:
it will be iterable
it will keep scores automatically sorted
It should work easily with consistente between equals, hashCode and compareTo but maybe you should tweak something (since it's untested code).
Consider the following question on storing values with duplicate keys:
Suppose there is a class Employee with name, sal and dob as attributes. I want to store the objects of Employee in a Map and the key would be the Employee name. The name can be duplicate.
Also after adding 10 objects in the Map. I want to retrieve the 8th object that was entered.
This is one solution to add objects with duplicate keys but for the 2nd part of the question, this would not work since on displaying the map, all values with the same key will be displayed together.
How would we maintain the order in which the objects were added in this situation? Can we modify equals and hashcode methods to somehow add the elements and then later retrieve them in the order in which they were inserted?
I think a LinkedHashMultimap (from Guava) should work for this. You wouldn't be able to get the 8th entry by index directly, but you could use something like Iterables.get(Iterable iterable, int position) to get it.
Why not just have two containers? One for mapping name to employee (like the one in the stackoverflow question you mentioned), another for mapping number to employee. You can make an "outer" container aggregating multimap and arraylist.
What you intend to do can be easily implemented using an ArrayList. This is the data structure that you should use.
The requirements are somehow contradictory. At one side several values should be possible for one key, at the other side only one value should be returned for a key. Additionaly, retrievals for a sequence should be possible. I see the nearest approximation in designing a dedicated data structure containing a hash map for fast access based on the name, and a list keeping the order of insertions. The access would be based on the overall sequence number or on the name plus index for the name. The implementation would be according the following lines:
public class Employee {
public String name; public int sal;
public Employee() {name = ""; sal = 0;}
public Employee(String name, int sal) {
this.name = name; this.sal = sal;
}
#Override public String toString() {return "(" + name + "," + sal + ")";}
}
public class Team {
private Map<String, ArrayList<Employee>> employees =
new HashMap<String, ArrayList<Employee>>();
private ArrayList<Employee> order = new ArrayList<Employee>();
public void addEmployee(Employee e) {
ArrayList<Employee> list = employees.get(e.name);
if (list == null) {
list = new ArrayList<Employee>();
employees.put(e.name, list);
}
list.add(e);
order.add(e);
}
public int getNumEmployees() {return order.size();}
public Employee getEmployee(int n) {return order.get(n - 1);}
public int getNumEmployees(String name) {
ArrayList<Employee> list = employees.get(name);
return list == null ? 0 : list.size();
}
public Employee getEmployee(String name, int n) {
ArrayList<Employee> list = employees.get(name);
return list == null ? null : list.get(n - 1);
}
}
// Test:
Team team = new Team();
team.addEmployee(new Employee("Bob", 11));
team.addEmployee(new Employee("Bob", 12));
team.addEmployee(new Employee("Eve", 13));
team.addEmployee(new Employee("Eve", 14));
System.out.println("Num all: " + team.getNumEmployees());
System.out.println("3rd: " + team.getEmployee(3));
System.out.println("Num Bobs: " + team.getNumEmployees("Bob"));
System.out.println("2nd Bob: " + team.getEmployee("Bob", 2));
Hi guys i've never written a comparator b4 and im having a real problem. I've created a hashtable.
Hashtable <String, Objects> ht;
Could someone show how you'd write a comparator for a Hashtable? the examples i've seen overide equals and everything but i simply dont have a clue. The code below is not mine but an example i found, the key thing in hashtables means i cant do it like this i guess.
public class Comparator implements Comparable<Name> {
private final String firstName, lastName;
public void Name(String firstName, String lastName) {
if (firstName == null || lastName == null)
throw new NullPointerException();
this.firstName = firstName;
this.lastName = lastName;
}
public String firstName() { return firstName; }
public String lastName() { return lastName; }
public boolean equals(Object o) {
if (!(o instanceof Name))
return false;
Name n = (Name)o;
return n.firstName.equals(firstName) &&
n.lastName.equals(lastName);
}
public int hashCode() {
return 31*firstName.hashCode() + lastName.hashCode();
}
public String toString() {
return firstName + " " + lastName;
}
public int compareTo(Name n) {
int lastCmp = lastName.compareTo(n.lastName);
return (lastCmp != 0 ? lastCmp :
firstName.compareTo(n.firstName));
}
}
A Comparator will tell you which of two items is larger. If this has meaning for your HashTable, only you can say what the meaning is. It would be very unusual to want to compare two HashTables in this way.
That's not a Comparator class. That's a Name class that implements Comparable.
Hashtable and Hashmap don't use either Comparator or Comparable. If you want sorted keys use a TreeMap.
Comparators are used to sort a list. A Hashtable (note the case) is not ordered by its elements. You can order a table by iterating over its keys (in the case you'd want to order on its keys, I presume) and put them in a List. The next thing to do is to sort the List and iterate over the List, and use a get out of the Hashtable to get its associated value.
Here is an example (using HashMap, since it's more integrated with the rest of the Java Collections. A HashMap is essentially the same as Hashtable.):
public static void main(String... arg) {
HashMap<String, Object> x = new HashMap<String, Object>();
x.put("second", " ordered!");
x.put("first", "Correctly");
LinkedList<String> keys = new LinkedList<String>();
for(final String f : x.keySet()) {
keys.add(f);
}
Collections.sort(keys, new Comparator<String>() {
public int compare(String first, String second) {
// return -1 is "first < second"
// return 1 is "first > second"
// return 0 is "first == second"
return first.compareTo(second);
}
});
for(final String f : keys) {
System.out.print(x.get(f));
}
System.out.println();
}
The order of the list keys is sorted by the anonymous Comparator class. It will sort alphabetically, as is the default for Strings. You can use your own key object, like you mentioned. If you don't implement Comparator in this key object, then you can supply, as in the above example. Else you can use the default Comparator by calling:
Collections.sort(keys);
Which will use the classes implementation of Comparator. If it does not implement Comparator, then it will throw an exception (since it will cast to a Comparator)