I'm surprised by the order my HashMap comes out.
I'm getting
Chandler
Joe
Ross
1
whereas I expected
Joe
Chandler
Ross
1
in line with the order I added data.
Can you explain this issue?
java code:
HashMap testing:
public class HashCodeTest {
static int count = 0;
public static void main(String[] args) {
Map employees = new HashMap();
employees.put(new Employee("Joe"), new Integer("1"));
employees.put(new Employee("Chandler"), new Integer("2"));
employees.put(new Employee("Chandler"), new Integer("2"));
employees.put(new Employee("Ross"), new Integer("3"));
Iterator iterator = employees.keySet().iterator();
while (iterator. hasNext()) {
System.out.println(iterator.next());
}
System.out.println(count);
}
}
class for map key:
import java.util.*;
class Employee {
private String name;
public Employee(String name){
this.name = name;
}
public String toString(){
return name;
}
public boolean equals(Object obj){
HashCodeTest.count++;
if (obj == null) {
return false;
}
if (obj.getClass() != getClass()){
return false;
}
Employee emp = (Employee)obj;
if (this.name == emp.name){
return true;
}
return false;
}
public int hashCode(){
return name.hashCode();
}
}
The ordering of a plain HashMap is not defined:
This class makes no guarantees as to the order of the map; in particular, it does not guarantee that the order will remain constant over time.
You can instead use a LinkedHashMap, which guarantees an iteration order equal to the order of insertion:
Hash table and linked list implementation of the Map interface, with predictable iteration order. This implementation differs from HashMap in that it maintains a doubly-linked list running through all of its entries. This linked list defines the iteration ordering, which is normally the order in which keys were inserted into the map (insertion-order).
...
This implementation spares its clients from the unspecified, generally chaotic ordering provided by HashMap (and Hashtable), without incurring the increased cost associated with TreeMap.
A basic HashMap is not ordered. However, there are TreeMaps, which are ordered by key, and LinkedHashMap which are ordered by insertion.
Related
Common question: How to use different Comparators of the custom class for sorting sequence its objects in PriorityQueue?
I tried to do that using this comparators in appropriate pairs of priorityqueues and lists of the objects with expected similar sorting results in the next code:
class User{
private Integer id;
private String name;
public User(Integer i, String n){
this.id=i;
this.name=n;
}
public Integer getId() {return id;}
public String getName() {return name;}
#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(id == null){
if (other.id != null)return false;
}else if(!id.equals(other.id))return false;
return true;
}
#Override
public String toString() {return "[id:" + id + ", name:" + name + "]";}
}
public class MyPriorityQueue {
public static Comparator<User> cmpId = Comparator.comparingInt(x -> x.getId());
public static Comparator<User> cmpNameLength = Comparator.comparingInt(x -> x.getName().length());
public static void main(String[] args) {
List<User> users = new ArrayList<User>(10);
users.add(new User(1,"11111"));
users.add(new User(3,"333"));
users.add(new User(5,"5"));
users.add(new User(4,"44"));
users.add(new User(2,"2222"));
Queue<User> ids = new PriorityQueue<User>(10, cmpId); //use first comparator
users.forEach(x-> ids.offer(x));
Queue<User> names = new PriorityQueue<User>(10, cmpNameLength); //use second comparator
names.addAll(users);
System.out.println("Variant_1.1:");
ids.forEach(System.out::println);
System.out.println("Variant_2.1:");
names.forEach(System.out::println);
System.out.println("Variant_1.2:");
users.sort(cmpId); //use first comparator
users.forEach(System.out::println);
System.out.println("Variant_2.2:");
users.sort(cmpNameLength); //use second comparator
users.forEach(System.out::println);
}
}
Output:
Variant_1.1: //Failed sorted queue by user.id with using comporator cmpId
[id:1, name:11111]
[id:2, name:2222]
[id:5, name:5]
[id:4, name:44]
[id:3, name:333]
Variant_2.1: //Failed sorted queue by length of the user.name with cmpNameLength
[id:5, name:5]
[id:4, name:44]
[id:3, name:333]
[id:1, name:11111]
[id:2, name:2222]
Variant_1.2: // OK: correctly sorted list by user.id with cmpId comporator
[id:1, name:11111]
[id:2, name:2222]
[id:3, name:333]
[id:4, name:44]
[id:5, name:5]
Variant_2.2: //OK: for list by length of the user.name with cmpNameLength
[id:5, name:5]
[id:4, name:44]
[id:3, name:333]
[id:2, name:2222]
[id:1, name:11111]
I expected that the:
results of the variant 1.1 and 2.1;
results of the variant 1.2 and 2.2;
will be same, but they were different.
My questions: What have I done wrong for ordering priorytyqueue/comparator and How to get sorting result for the priorityqueue as for the appropriate list in my example?
You haven't done anything wrong, it's just that PriorityQueue's iterator is:
not guaranteed to traverse the elements of the priority queue in any particular order (Javadoc)
The forEach method internally uses the iterator, so the same problem exists.
This is because the underlying data structure is such that it "sorts" as you deque items. If the implementor wanted to return items in sorted order, they would have had to first collect the items, and then sort them before returning them. This incurs a performance hit, and so (I presume) it was decided to return it unordered, because PriorityQueue is primarily a queue, rather than a sorted collection, and a user could always sort the item themselves (which is as efficient as it gets).
In order to obtain the elements ordered, do something like:
while(pq.peek() != null){
System.out.println(pq.poll());
}
In your code sample, you are using Iterable#forEach to iterate through the queues.
ids.forEach(System.out::println);
names.forEach(System.out::println);
forEach ultimately delegates into Iterable#iterator. However, it's important to note that the subclass override in PriorityQueue#iterator has different JavaDocs with a special note about ordering.
Returns an iterator over the elements in this queue. The iterator does not return the elements in any particular order.
In other words, there is no guarantee that iterating over a PriorityQueue will use your Comparator. If instead you changed your code to drain the queue by repeatedly calling PriorityQueue#poll, then I expect you'd see results ordered according to your custom Comparator.
Digging into the OpenJDK source, we can see that the internal data structure inside PriorityQueue is a binary heap. This is backed by an array, and as callers add and remove elements of the queue, the code internally maintains the heap invariant.
/**
* Priority queue represented as a balanced binary heap: the two
* children of queue[n] are queue[2*n+1] and queue[2*(n+1)]. The
* priority queue is ordered by comparator, or by the elements'
* natural ordering, if comparator is null: For each node n in the
* heap and each descendant d of n, n <= d. The element with the
* lowest value is in queue[0], assuming the queue is nonempty.
*/
transient Object[] queue; // non-private to simplify nested class access
However, the internal Iterator implementation simply uses an integer cursor to scan forward through that array, with no consideration of element priorities or heap layout.
return (E) queue[lastRet = cursor++];
so this is my hashmap
public HashMap<Integer, HashMap<String, Integer>> girls =
new HashMap<Integer, HashMap<String, **Integer**>>();;
I want to sort the bolded by value. For clarification the outer hashMaps key stands for a year a girl child was born and the inner hashmap stands for a name mapped to the popularity ranking of the name.
So let's say that in 2015, the name Abigail was given to 47373 babies and it was the most popular name in that year, I'd want to return the number 1 bc it's the number one name. Is there any way to sort a hashmap in this way?
how would I turn the inner hashmaps values into an arraylist that I could then easily sort? Any help?
There is no easy/elegant way to sort a Map by value in its data structure.
HashMaps are unsorted by definition.
LinkedHashMaps are sorted by insertion order.
TreeMaps are sorted by key.
If you really need to, you could write an algorithm which builds up you data structure using a LinkedHashMap as the "inner" structure and make sure the largest value is inserted first.
Alternatively, you could write a small class
class NameFrequency
{
String name;
int frequency;
}
and make your data structure a HashMap<Integer, TreeSet<NameFrequency>> and define a comparator for the TreeSet which orders those objects the way you like.
Or, finally, you could leave your data structure as it is and only order it when accessing it:
girls.get(2015).entrySet().stream()
.sorted((entry1, entry2) -> entry2.getValue() - entry1.getValue())
.forEachOrdered(entry -> System.out.println(entry.getKey() + ": " + entry.getValue()));
You're better off just creating a class for a name and number of occurrences.
import java.util.Objects;
public class NameCount implements Comparable<NameCount> {
private final String name;
private int count;
public NameCount(String name) {
this.name = name;
count = 0;
}
public NameCount(String name, int count) {
this.name = name;
this.count = count;
}
public int getCount() {
return count;
}
public void setCount(int count) {
this.count = count;
}
public void incrementCount() {
count++;
}
#Override
public int hashCode() {
return Objects.hashCode(this.name);
}
#Override
public boolean equals(Object obj) {
if(obj == null) return false;
if(getClass() != obj.getClass()) return false;
final NameCount other = (NameCount)obj;
if(!Objects.equals(this.name, other.name)) return false;
return true;
}
#Override
public int compareTo(NameCount o) {
return o.count - count;
}
}
You can then define your map as Map<Integer, List<NameCount>>. Note how the above class defines equality and hash code based only on the name, so if you want to see if a name is in a list, you can just create a NameCount for it and use contains. The compareTo implementation orders from higher count to lower, so when getting the List<NameCount> for a given year, you can then use Collections.sort(list) on it and ask for the index for a NameCount with the same name.
public void test(Map<Integer, List<NameCount>> map) {
int year = 2017;
List<NameCount> list = map.get(year);
// Do null-check on list first when using this...
Collections.sort(list);
NameCount check = new NameCount("Abigail");
int rank = list.indexOf(check) + 1;
}
It might seem to make more sense to use TreeSet map values to guarantee unique name entries and keep them sorted all the time, but note that TreeSet defines equality based on comparison, not equals, and it wouldn't let you get the index.
Sorry if this question is not clear. I have very limited knowledge about hashmap. This was the question which was asked to me in the interview.
If all objects return same hascode and If we use those objects as key to store in hashmap. How sorting works in this case?
My understanding was if hashcode are same then new entry will replace the old entry in the hashmap.
But when i searched to understand more about how hashmap works if the two objects hashcode is same, i foudn that both objects will be stored in the linked list form in the bucket.
But its not clear how sorting will work in this case. If we try to sort this hashpMap using TreeMap.
Please help me to understand this.
Below code stores more than one entry in the hashmap where all the objects hashcode is same.
import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;
public class Employee implements Comparable
{
private String name;
private int age;
public Employee(String name, int age)
{
this.name = name;
this.age = age;
}
#Override
public String toString()
{
return name + ": " + age;
}
#Override
public int hashCode(){
return 1;
}
#Override
public boolean equals(Object o){
if (!(o instanceof Employee))
return false;
Employee e = (Employee) o;
return e.getName().equals(name) && e.getAge() == age;
}
String getName()
{
return name;
}
int getAge()
{
return age;
}
public static void main(String a[]){
Employee e = new Employee("Sub" , 25);
Employee e1 = new Employee("Sub1" , 20);
Employee e2 = new Employee("Sub2" , 22);
System.out.println(e);
Map m = new HashMap();
m.put(e , "A");
m.put(e1 , "B");
m.put(e2 , "C");
System.out.println(m);
TreeMap t = new TreeMap(m);
System.out.println(t);
}
#Override
public int compareTo(Object arg0) {
return ((Employee)arg0).getName().compareTo(this.name);
}
}
Objects are distributed on the storage space according to the hash value obtained from their hashcode function. If all objects return the same hashcode, you'll have a collision for each object you add to the HashMap. Also you'll have a sequential search (using equals method) for every object you get from the Hashmap.
In short: change your hashcode algorithm to reduce collisions.
If you need to add the same key but with different values I will recommend you to use MultiMap from apache commons or guava. I found it more useful because you can use ListMultimap (Guava) and then it doesnt mather if you add the same key the values will be added to a list. I know this is not the question but I think you asked it because you have some issue with it.
hashmap is not store elements in sorting order.
For storing it fisrt look for the appropriete bucket with the help of hashcode.
if hashcode are same it will store the element in linklist format and with utilization of single bucket only.
I need a Collection that sorts the element, but does not removes the duplicates.
I have gone for a TreeSet, since TreeSet actually adds the values to a backed TreeMap:
public boolean add(E e) {
return m.put(e, PRESENT)==null;
}
And the TreeMap removes the duplicates using the Comparators compare logic
I have written a Comparator that returns 1 instead of 0 in case of equal elements. Hence in the case of equal elements the TreeSet with this Comparator will not overwrite the duplicate and will just sort it.
I have tested it for simple String objects, but I need a Set of Custom objects.
public static void main(String[] args)
{
List<String> strList = Arrays.asList( new String[]{"d","b","c","z","s","b","d","a"} );
Set<String> strSet = new TreeSet<String>(new StringComparator());
strSet.addAll(strList);
System.out.println(strSet);
}
class StringComparator implements Comparator<String>
{
#Override
public int compare(String s1, String s2)
{
if(s1.compareTo(s2) == 0){
return 1;
}
else{
return s1.compareTo(s2);
}
}
}
Is this approach fine or is there a better way to achieve this?
EDIT
Actually I am having a ArrayList of the following class:
class Fund
{
String fundCode;
BigDecimal fundValue;
.....
public boolean equals(Object obj) {
// uses fundCode for equality
}
}
I need all the fundCode with highest fundValue
You can use a PriorityQueue.
PriorityQueue<Integer> pQueue = new PriorityQueue<Integer>();
PriorityQueue(): Creates a PriorityQueue with the default initial capacity (11) that orders its elements according to their natural ordering.
This is a link to doc: https://docs.oracle.com/javase/8/docs/api/java/util/PriorityQueue.html
I need all the fundCode with highest fundValue
If that's the only reason why you want to sort I would recommend not to sort at all. Sorting comes mostly with a complexity of O(n log(n)). Finding the maximum has only a complexity of O(n) and is implemented in a simple iteration over your list:
List<Fund> maxFunds = new ArrayList<Fund>();
int max = 0;
for (Fund fund : funds) {
if (fund.getFundValue() > max) {
maxFunds.clear();
max = fund.getFundValue();
}
if (fund.getFundValue() == max) {
maxFunds.add(fund);
}
}
You can avoid that code by using a third level library like Guava. See: How to get max() element from List in Guava
you can sort a List using Collections.sort.
given your Fund:
List<Fund> sortMe = new ArrayList(...);
Collections.sort(sortMe, new Comparator<Fund>() {
#Override
public int compare(Fund left, Fund right) {
return left.fundValue.compareTo(right.fundValue);
}
});
// sortMe is now sorted
In case of TreeSet either Comparator or Comparable is used to compare and store objects . Equals are not called and that is why it does not recognize the duplicate one
Instead of the TreeSet we can use List and implement the Comparable interface.
public class Fund implements Comparable<Fund> {
String fundCode;
int fundValue;
public Fund(String fundCode, int fundValue) {
super();
this.fundCode = fundCode;
this.fundValue = fundValue;
}
public String getFundCode() {
return fundCode;
}
public void setFundCode(String fundCode) {
this.fundCode = fundCode;
}
public int getFundValue() {
return fundValue;
}
public void setFundValue(int fundValue) {
this.fundValue = fundValue;
}
public int compareTo(Fund compareFund) {
int compare = ((Fund) compareFund).getFundValue();
return compare - this.fundValue;
}
public static void main(String args[]){
List<Fund> funds = new ArrayList<Fund>();
Fund fund1 = new Fund("a",100);
Fund fund2 = new Fund("b",20);
Fund fund3 = new Fund("c",70);
Fund fund4 = new Fund("a",100);
funds.add(fund1);
funds.add(fund2);
funds.add(fund3);
funds.add(fund4);
Collections.sort(funds);
for(Fund fund : funds){
System.out.println("Fund code: " + fund.getFundCode() + " Fund value : " + fund.getFundValue());
}
}
}
Add the elements to the arraylist and then sort the elements using utility Collections.sort,. then implement comparable and write your own compareTo method according to your key.
wont remove duplicates as well, can be sorted also:
List<Integer> list = new ArrayList<>();
Collections.sort(list,new Comparator<Integer>()
{
#Override
public int compare(Objectleft, Object right) {
**your logic**
return '';
}
}
)
;
I found a way to get TreeSet to store duplicate keys.
The problem originated when I wrote some code in python using SortedContainers. I have a spatial index of objects where I want to find all objects between a start/end longitude.
The longitudes could be duplicates but I still need the ability to efficiently add/remove specific objects from the index. Unfortunately I could not find the Java equivalent of the Python SortedKeyList that separates the sort key from the type being stored.
To illustrate this consider that we have a large list of retail purchases and we want to get all purchases where the cost is in a specific range.
// We are using TreeSet as a SortedList
TreeSet _index = new TreeSet<PriceBase>()
// populate the index with the purchases.
// Note that 2 of these have the same cost
_index.add(new Purchase("candy", 1.03));
Purchase _bananas = new Purchase("bananas", 1.45);
_index.add(new Purchase(_bananas);
_index.add(new Purchase("celery", 1.45));
_index.add(new Purchase("chicken", 4.99));
// Range scan. This iterator should return "candy", "bananas", "celery"
NavigableSet<PriceBase> _iterator = _index.subset(
new PriceKey(0.99), new PriceKey(3.99));
// we can also remove specific items from the list and
// it finds the specific object even through the sort
// key is the same
_index.remove(_bananas);
There are 3 classes created for the list
PriceBase: Base class that returns the sort key (the price).
Purchase: subclass that contains transaction data.
PriceKey: subclass used for the range search.
When I initially implemented this with TreeSet it worked except in the case where the prices are the same. The trick is to define the compareTo() so that it is polymorphic:
If we are comparing Purchase to PriceKey then only compare the price.
If we are comparing Purchase to Purchase then compare the price and the name if the prices are the same.
For example here are the compareTo() functions for the PriceBase and Purchase classes.
// in PriceBase
#Override
public int compareTo(PriceBase _other) {
return Double.compare(this.getPrice(), _other.getPrice());
}
// in Purchase
#Override
public int compareTo(PriceBase _other) {
// compare by price
int _compare = super.compareTo(_other);
if(_compare != 0) {
// prices are not equal
return _compare;
}
if(_other instanceof Purchase == false) {
throw new RuntimeException("Right compare must be a Purchase");
}
// compare by item name
Purchase _otherPurchase = (Purchase)_other;
return this.getName().compareTo(_otherChild.getName());
}
This trick allows the TreeSet to sort the purchases by price but still do a real comparison when one needs to be uniquely identified.
In summary I needed an object index to support a range scan where the key is a continuous value like double and add/remove is efficient.
I understand there are many other ways to solve this problem but I wanted to avoid writing my own tree class. My solution seems like a hack and I am surprised that I can't find anything else. if you know of a better way then please comment.
Why am getting a ClassCastException at (Person p2 = (Person) o2;) in overridden compare method . :(
Actually Instead of Person Object the values in compare overridden method is coming as "Jim" and "Jack" (Key values). So the Cast Cast Exception . But Why is it coming with keys not values i,e the Person object , Why is it only applied for keys . Are there any other way to sort it based on values .
Please correct me if am wrong
1) We can Pass the comparator object in the TreeMap which will sort it accordingly.?
2) Always the Sorting is performed over Keys . ?
3) How can we sort a Map over its values without using anymore collection object (Is it possible) and why is not supported by default ?
public class HashTableExamples {
/**
* #param args
*/
public static void main(String[] args) {
SortedMap persorSorted = new TreeMap(new Comparator() {
#Override
public int compare(Object o1, Object o2) {
Person p2 = (Person) o2;
return 2;
}
});
Person p = new Person(10);
Person p1 = new Person(20);
persorSorted.put("Jim", p);
persorSorted.put("Jack", p1);
Iterator sortedit = persorSorted.entrySet().iterator();
while (sortedit.hasNext()) {
Map.Entry pairs = (Map.Entry) sortedit.next();
Person pw = (Person) pairs.getValue();
System.out.println("From SortedMap : " + pw.getAge());
}
}
public static class Person {
Person(int agevalue) {
this.age = agevalue;
}
private int age;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
Yes, TreeMap always sorts on keys.
As to why it's not "supported by default" -- it's because there doesn't exist a data structure in general that supports it efficiently. It's not supported efficiently in any programming language, because the point of a Map is to be able to look things up by their key, and sorting by values means you can't organize the data in a way that makes it efficient to look things up by keys.
If you must sort a Map's entries by value, you can use something like this:
List<Map.Entry<Foo, Bar>> entryList =
new ArrayList<Map.Entry<Foo, Bar>>(map.entrySet());
Collections.sort(entryList, new Comparator<Map.Entry<Foo, Bar>>() {
public int compare(Map.Entry<Foo, Bar> entry1, Map.Entry<Foo, Bar> entry2) {
return entry1.getValue().compareTo(entry2.getValue());
}
});
Alternately, if you like, you can use an alternate comparator to compare the values if you don't control the implementation of the value type.
If you look at the documentation for TreeMap you'll see it says:
Constructs a new, empty tree map, ordered according to the given comparator. All keys inserted into the map must be mutually comparable by the given comparator: comparator.compare(k1, k2) must not throw a ClassCastException for any keys k1 and k2 in the map. If the user attempts to put a key into the map that violates this constraint, the put(Object key, Object value) call will throw a ClassCastException.
The main point here is that it's comparing keys, but you're casting the key (ie: a String) into a Person.