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.
Related
This is what i have so far, i'm trying to sort a bunch of List<String>'s based on the value of an index.
LinkedHashSet<List<String>> sorted = new LinkedHashSet<List<String>>();
How do i sort the LinkedHashSet in order from Highest to Lowest index 2 value of the List's?
Example input:
List<String> data1 = Database.getData(uuid);
double price = Double.valueOf(data1.get(2))
data1.add("testval");
data1.add("testval");
data1.add("100.00");
sorted.add(data1);
and on another seperate List:
List<String> data2 = Database.getData(uuid);
double price = Double.valueOf(data2.get(2))
data2.add("anotherval");
data2.add("anotherval");
data2.add("50.00");
sorted.add(data2);
Output of the sorted LinkedHashSet in descending order.
testval testval 100.00
anotherval anotherval 50.00
Sorry if this is confusing, im not sure where to go about sorting like this.
Create a new class to represent you complex objects. There is no need to store multiple values in a list when you can do it in objects.
public class ComplexObject {
private String description1;
private String description2;
private Double value;
public ComplexObject(String description1, String description2, Double value) {
this.description1 = description1;
this.description2 = description2;
this.value = value;
}
public void setDescription1(String description1) {
this.description1 = description1;
}
public String getDescription1() {
return description1;
}
public void setDescription2(String description2) {
this.description2 = description2;
}
public String getDescription2() {
return description2;
}
public void setValue(Double value) {
this.value = value;
}
public Double getValue() {
return value;
}
}
Then add elements to the list and sort it using a new, custom, comparator:
public static void main(String[] args) {
List<ComplexObject> complexObjectList = new ArrayList<ComplexObject>();
//add elements to the list
complexObjectList.add(new ComplexObject("testval","testval",100.00d));
complexObjectList.add(new ComplexObject("anotherval","anotherval",50.00d));
//sort the list in descending order based on the value attribute of complexObject
Collections.sort(complexObjectList, new Comparator<ComplexObject>() {
public int compare(ComplexObject obj1, ComplexObject obj2) {
return obj2.getValue().compareTo(obj1.getValue()); //compares 2 Double values, -1 if less , 0 if equal, 1 if greater
}
});
//print objects from sorted list
for(ComplexObject co : complexObjectList){
System.out.println(co.getDescription1()+" "+co.getDescription2()+" "+co.getValue());
}
}
Output:
testval testval 100.0
anotherval anotherval 50.0
Firstly, you shouldn't use a LinkedHashSet but a TreeSet. LinkedHashSet will retain the insertion order without sorting.
Secondly, you need to initialize your TreeSet with a Comparator that compares based on whichever value of your List is required, that is, if you know the index of the String that will represent a double value in advance. Otherwise I would recommend using custom objects instead of List.
If you decide to use custom objects, you don't necessarily need to initialize your TreeSet with a Comparator as second argument.
Instead, you could have your custom objects implement Comparable, and implement a one-time comparation logic there.
It all depends on whether you only need to sort in a particular order.
Finally, custom objects will require you to override equals and hashCode.
First, and extracted from Oracle's Java reference:
This linked list defines the iteration ordering, which is the order in which elements were inserted into the set
So you can't sort your data just inserting it into the LinkedHashSet.
You may be confusing that set implementation with SortedSet. SortedSet allows you to pass a comparator which will determine the elements order in the data structure.
On the other hand, I don't know whether you chose you List<String> arbitrarily but it seems to me a wiser option to aggregate your the 3 strings as a class attributes. The point is that, if your elements are always going to be 3 elements, being the last one a double value: Why do you need a dynamic structure as a List?
EDIT
Here you have a possible better implementation of what you want:
public class Element
{
public Element(String a, String b, double val) {
this.a = a;
this.b = b;
this.val = val;
}
#Override
public String toString() {
return a + "\t" + b + "\t" + val;
}
public String a;
public String b;
public double val;
}
And you can use this class to store your elements. An example of use:
SortedSet<Element> sorted = new TreeSet<>(new Comparator<Element>() {
#Override
public int compare(Element o1, Element o2) {
return (new Double(o1.val)).compareTo(o2.val);
}
});
sorted.add(new Element("testval", "testval", 100.0));
sorted.add(new Element("anotherval", "anotherval", 50.0));
for(Element el: sorted)
{
System.out.println(el);
}
Note that the comparator is given as an instance of an anonympous inner class implementing Java's Comparator interface.
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.
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.
I have been running into this problem sometimes when programming.
Imagine I have a table of data with two columns. The first column has strings, the second column has integers.
I want to be able to store each row of the table into a dynamic array. So each element of the array needs to hold a string and an integer.
Previously, I have been accomplishing this by just splitting each column of the table into two separate ArrayLists and then when I want to add a row, I would call the add() method once on each ArrayList. To remove, I would call the remove(index) method once on each ArrayList at the same index.
But isn't there a better way? I know there are classes like HashMap but they don't allow duplicate keys. I am looking for something that allows duplicate entries.
I know that it's possible to do something like this:
ArrayList<Object[]> myArray = new ArrayList<Object[]>();
myArray.add(new Object[]{"string", 123});
I don't really want to have to cast into String and Integer every time I get an element out of the array but maybe this is the only way without creating my own? This looks more confusing to me and I'd prefer using two ArrayLists.
So is there any Java object like ArrayList where it would work like this:
ArrayList<String, Integer> myArray = new ArrayList<String, Integer>();
myArray.add("string", 123);
Just create simple POJO class to hold row data. Don't forget about equals and hashCode and prefer immutable solution (without setters):
public class Pair {
private String key;
private Integer value;
public Pair(String key, Integer value) {
this.key = key;
this.value = value;
}
public String getKey() {
return key;
}
public Integer getValue() {
return value;
}
// autogenerated
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Pair)) return false;
Pair pair = (Pair) o;
if (key != null ? !key.equals(pair.key) : pair.key != null) return false;
if (value != null ? !value.equals(pair.value) : pair.value != null) return false;
return true;
}
#Override
public int hashCode() {
int result = key != null ? key.hashCode() : 0;
result = 31 * result + (value != null ? value.hashCode() : 0);
return result;
}
}
Usage:
List<Pair> list = new ArrayList<Pair>();
list.add(new Pair("string", 123));
Note: in other languages there are build-in solutions for it like case-classes and tuples in Scala.
Create a Row class that holds the data.
package com.stackoverflow;
import java.util.ArrayList;
import java.util.List;
/**
* #author maba, 2012-10-10
*/
public class Row {
private int intValue;
private String stringValue;
public Row(String stringValue, int intValue) {
this.intValue = intValue;
this.stringValue = stringValue;
}
public int getIntValue() {
return intValue;
}
public String getStringValue() {
return stringValue;
}
public static void main(String[] args) {
List<Row> rows = new ArrayList<Row>();
rows.add(new Row("string", 123));
}
}
You can create very simple object, like :
public class Row{
private String strVal;
private Integer intVal;
public Row(String s, Integer i){
strVal = s;
intVal = i;
}
//getters and setters
}
Then use it as follows :
ArrayList<Row> myArray = new ArrayList<Row>();
myArray.add(new Row("string", 123));
Map is the option if you are sure that any one value among integer or string is unique. Then you can put that unique value as a key. If it is not true for your case, creating a simple POJO is best option for you. Infact, if in future, there a chance to come more values (columns) per row then also using a POJO will be less time consuming. You can define POJO like;
public class Data {
private int intValue;
private String strValue;
public int getIntValue() {
return intValue;
}
public void setIntValue(int newInt) {
this.intValue = newInt;
}
public String getStrValue() {
return strValue;
}
public void setStrValue(String newStr) {
this.strValue = newStr;
}
And in the class you can use it like;
ArrayList<Data> dataList = new ArrayList<Data>();
Data data = new Data();
data.setIntValue(123);
data.setStrValue("string");
dataList.add(data);
You should create a class (e.g. Foo) that contains an int and a String.
Then you can create an ArrayList of Foo objects.
List<Foo> fooList = new ArrayList<Foo>();
This is called a map my friend. It is similar to a dictionary in .net
http://docs.oracle.com/javase/6/docs/api/java/util/Map.html
HashMap my be the class you are looking for assuming "string" going to different for different values. Here is documentation on HashMap
Example:
HashMap<String, Integer> tempMap = new HashMap<String, Integer>();
tempMap.put("string", 124);
If you need to add more than one value, you may create HashMap<String, ArrayList> like that.
you can use google collection library Guava there is a Map called Multimap. It is collection similar to a Map, but which may associate multiple values with a single key. If you call put(K, V) twice, with the same key but different values, the multimap contains mappings from the key to both values.
Use Map to solve this problem:
Map<String, Integer> map = new HashMap<String, Integer>();
Eg:
map.put("string", 123);