How to not lose elements with same values from a TreeMap? - java

class CompoundKey implements Comparable<CompoundKey>{
String key;
Integer count;
public CompoundKey(String key, Integer count){
this.key = key;
this.count = count;
}
#Override
public int compareTo(#Nonnull CompoundKey other) {
return (other.count.compareTo(this.count));
}
}
public static void main(String[] args) {
Map<CompoundKey, Integer> map = new TreeMap<>();
map.put(new CompoundKey("a", 3), 3);
map.put(new CompoundKey("b", 1), 1);
map.put(new CompoundKey("c", 8), 8);
map.put(new CompoundKey("d", 3), 3);
map.put(new CompoundKey("e", 9), 9);
for (CompoundKey key : map.keySet()) {
System.out.println(key.key + "->" + map.get(key));
}
}
This will print out as below:
e->9
c->8
a->3
b->1
In the print out, the 'd->3' is missing. The purpose of this implementation is to create a map sorted by value when element is inserted (I don't need an implementation that will sort the map after all are inserted).
Is there some minor modification of my code to not lose the element with duplicate values? In the case of two duplicate values, the sorting order can be random.

Be sure to factor in the String as part of your Comparable. For instance (your exact logic may want to vary):
public int compareTo(CompoundKey other) {
return other.count.compareTo(this.count) + other.key.compareTo(this.key);
}
Because it only looks at numbers right now, it will only ever count numerals as being the natural order. You need to include key as a part of this.

Related

Comparator creates duplicates in TreeMap

I would like to sort my HashMap (or TreeMap) by values. I kind of achieved this by creating a custom Comparator that sorts after value. However whenever I put in all my entries from the HashMap again I get duplicates.
How can I sort by values without creating duplicates?
CODE
public class Test {
public static void main(String[] args) {
HashMap<Integer, String> hMap = new HashMap<Integer, String>();
ValueComparator vc = new ValueComparator(hMap);
TreeMap<Integer, String> tMap = new TreeMap<Integer, String>(vc);
hMap.put(0, "b");
hMap.put(1, "c");
hMap.put(2, "a");
tMap.putAll(hMap);
tMap.putAll(hMap);
for (Map.Entry<Integer, String> entry : tMap.entrySet()) {
System.out.println(entry.getKey() + " " + entry.getValue());
}
}
}
class ValueComparator implements Comparator<Integer> {
Map<Integer, String> base;
public ValueComparator(Map<Integer, String> base) {
this.base = base;
}
public int compare(Integer a, Integer b) {
if (base.get(a).charAt(0) >= base.get(b).charAt(0))
return 1;
else return -1;
}
}
OUTPUT
2 a
2 a
0 b
0 b
1 c
1 c
You need to modify logic as below, handle all three cases of -1, 0 and 1
public int compare(Integer a, Integer b) {
if (base.get(a).charAt(0) == base.get(b).charAt(0))
return 0;
else if (base.get(a).charAt(0) > base.get(b).charAt(0))
return 1;
else
return -1;
}
output
2 a
0 b
1 c
The compare method should return 0 if both objects are equal. In your implementation, you're returning 1, and thus the map does not recognize duplicates properly.
One way to solve this is to reuse Character.compare to compare two chars:
public int compare(Integer a, Integer b) {
return Character.compare
(base.get(a).charAt(0), base.get(b).charAt(0));
}
Your comparator contract is wrong. the compare method contract says:
Compares its two arguments for order. Returns a negative integer, zero, or a positive integer as the first argument is less than, equal to, or greater than the second.
Your code is only doing 1 and -1
what about the 0?

TreeMap - Dynamic ordering elements based on key

I want to use HashMap with ordering of elements. So I choose TreeMap. Below code gives me strange answer, than what I expected
public class MapTest {
public static class Key implements Comparable<Key>{
private String key;
private int count;
public Key(String key, int count){
this.key = key;
this.count = count;
}
#Override
public int hashCode() {
return key.hashCode();
}
#Override
public boolean equals(Object obj) {
return key.equals(obj);
}
#Override
public int compareTo(Key o) {
return count - o.count;
}
}
public static void main(String[] args) {
Map<Key, Integer> map = new TreeMap<>();
Key c = new Key("c", 4);
map.put(new Key("a", 6), 1);
map.put(new Key("b", 8), 1);
map.put(c, 1);
map.put(new Key("d", 2), 1);
for(Map.Entry<Key, Integer> entry : map.entrySet()){
System.out.println(entry.getKey().key);
}
//map.remove(c);
map.put(c, null);
c.count = 0;
map.put(c, 1);
for(Map.Entry<Key, Integer> entry : map.entrySet()){
System.out.println(entry.getKey().key);
}
}
}
If I use map.remove() and add element, it is ordered. Otherwise it is always returns the element in order
d c a b
Why above code is not working? put(key, null) should delete the value and if new value is inserted it has to be ordered right?
put(key, null) does not remove the key from the map. It is still in the map, just mapping to null. You want to remove(key).
Objects used as keys in a Map should be immutable really. You are modifying the key after you put it into the map - but the map has no mechanism to detect that and move the key so as you realized the key ends up at an invalid location.
This can then confuse the Map to the point that it doesn't think the key is present in the map at all since it goes to look for it where it should be and it isn't there.

How to sort a treemap and display its values and indices of the values and also skip to the next index if the previous one is same with the next

I have a TreeMap in which I have stored some values. The map is sorted using the values, from highest to lowest. Now I want print out the contents of the TreeMap with their various indices.
If I have the following pairs in the map :
("Andrew", 10),
("John", 5),
("Don",9),
("Rolex", 30),
("Jack", 10),
("Dan",9)
I want to print out:
Rolex, 30 , 1
Jack, 10, 2
Andrew, 10, 2
Dan, 9, 4
Don, 9, 4
John, 5, 6.
This is what I've been trying but it doesn't seem to work well:
/**
*
* #author Andrew
*/
import java.util.*;
public class SortArray {
static <K,V extends Comparable<? super V>> SortedSet<Map.Entry<K,V>>entriesSortedByValues(Map<K,V> map) {
SortedSet<Map.Entry<K,V>> sortedEntries = new TreeSet<Map.Entry<K,V>>(
new Comparator<Map.Entry<K,V>>() {
#Override public int compare(Map.Entry<K,V> e1, Map.Entry<K,V> e2) {
int res = e1.getValue().compareTo(e2.getValue());
return res!= 0 ? res : 1;
//return e1.getValue().compareTo(e2.getValue());
}
});
sortedEntries.addAll(map.entrySet());
return sortedEntries;
}
public void test(){
Map mm = new TreeMap();
mm.put("Andrew", 11);
mm.put("Mbata", 21);
mm.put("Chinedu", 14);
mm.put("Bol", 14);
mm.put("Don", 51);
mm.put("Rolex", 16);
mm.put("Son", 41);
SortedSet newMap = entriesSortedByValues(mm);
Iterator iter = newMap.iterator();
int x = newMap.size();
List names = new ArrayList();
List scores = new ArrayList();
while(iter.hasNext()){
String details = iter.next().toString();
StringTokenizer st = new StringTokenizer(details, "=");
String name = st.nextToken();
names.add(name);
String score = st.nextToken();
scores.add(score);
//System.out.println(name + " Score:" +score + " Position:" + x);
x--;
}
Collections.reverse(names);
Collections.reverse(scores);
int pos = 1;
for(int i = 0; i<names.size();){
try{
int y = i+1;
if(scores.get(i).equals(scores.get(y))){
System.out.print("Name: "+ names.get(i)+"\t");
System.out.print("Score: "+ scores.get(i)+"\t");
System.out.println("Position: "+ String.valueOf(pos));
//pos++;
i++;
continue;
} else{
System.out.print("Name: "+ names.get(i)+"\t");
System.out.print("Score: "+ scores.get(i)+"\t");
System.out.println("Position: "+ String.valueOf(pos++));
}
i++;
} catch(IndexOutOfBoundsException e) {}
}
}
public SortArray(){
test();
}
public static void main(String [] args){
new SortArray();
}
}
First of all, Why are you catching that IndexOutOfBoundsException and doing nothing with it? if you run that you'll get that exception thrown (and I thing you already know it) the problem is in your algorithm inside the last "for" loop. I shouldn't give you the solution, but wth... at least you did some effort to make it run, so this is a more less working version:
import java.util.*;
public class SortArray {
static <K,V extends Comparable<? super V>> SortedSet<Map.Entry<K,V>>entriesSortedByValues(Map<K,V> map) {
SortedSet<Map.Entry<K,V>> sortedEntries = new TreeSet<Map.Entry<K,V>>(
new Comparator<Map.Entry<K,V>>() {
#Override public int compare(Map.Entry<K,V> e1, Map.Entry<K,V> e2) {
int res = e1.getValue().compareTo(e2.getValue());
return res != 0 ? res : 1;
//return e1.getValue().compareTo(e2.getValue());
}
});
sortedEntries.addAll(map.entrySet());
return sortedEntries;
}
public void test(){
Map mm = new TreeMap();
mm.put("Andrew", 11);
mm.put("Mbata", 21);
mm.put("Chinedu", 14);
mm.put("Bol", 14);
mm.put("Don", 51);
mm.put("Rolex", 16);
mm.put("Son", 41);
SortedSet newMap = entriesSortedByValues(mm);
Iterator iter = newMap.iterator();
int x = newMap.size();
List names = new ArrayList();
List scores = new ArrayList();
while(iter.hasNext()){
String details = iter.next().toString();
StringTokenizer st = new StringTokenizer(details, "=");
String name = st.nextToken();
names.add(name);
String score = st.nextToken();
scores.add(score);
//System.out.println(name + " Score:" +score + " Position:" + x);
x--;
}
Collections.reverse(names);
Collections.reverse(scores);
int pos;
int posBis = 0;
String lastScore = "";
for(int i = 0; i<names.size(); i++){
System.out.print("Name: "+ names.get(i)+"\t");
System.out.print("Score: "+ scores.get(i)+"\t");
if(i == 0 || !lastScore.equals(scores.get(i))) {
pos = i + 1;
posBis = pos;
} else {
pos = posBis;
}
System.out.println("Position: "+ String.valueOf(pos));
lastScore = (String)scores.get(i);
}
}
public SortArray(){
test();
}
public static void main(String [] args){
new SortArray();
}
}
Your SortedSet is the wrong way to go about this. You can see in your Comparator that it gets a bit messy when both values have to be looked up by the same key then you've got this messy (and incorrect) return res != 0 ? res : 1 (the 1 should really be e1.getKey().compareTo(e2.getKey()) rather than always returning 1).
A better way to go about this would be to just sort the keys yourself in a List, rather than creating a separate SortedSet. This way you don't have to worry about duplicate sorting values.
You can also abstract out the Comparator stuff a little, to make it more reusable in other code later, if you need it.
import java.util.*;
public class PrintSomething {
public static <T extends Comparable<T>> Comparator<T> reverseComparator(final Comparator<T> oldComparator) {
return new Comparator<T>() {
#Override
public int compare(T o1, T o2) {
return oldComparator.compare(o2, o1);
}
};
}
public static <K,V extends Comparable<V>> Comparator<K> keyedComparator(final Map<K,V> lookup) {
return new Comparator<K>() {
#Override
public int compare(K o1, K o2) {
return lookup.get(o1).compareTo(lookup.get(o2));
}
};
}
public static void main(String[] args) {
Map<String, Integer> mm = new HashMap<>();
mm.put("Andrew", 10);
mm.put("John", 5);
mm.put("Don", 9);
mm.put("Rolex", 30);
mm.put("Jack", 10);
mm.put("Dan", 9);
Comparator<String> comparator = reverseComparator(keyedComparator(mm));
List<String> keys = Arrays.asList(mm.keySet().toArray(new String[mm.size()]));
//Collections.sort(keys); // optional, if you want the names to be alphabetical
Collections.sort(keys, comparator);
int rank = 1, count = 0;
Integer lastVal = null;
for (String key : keys) {
if (mm.get(key).equals(lastVal)) {
count++;
} else {
rank += count;
count = 1;
}
lastVal = mm.get(key);
System.out.println(key + ", " + mm.get(key) + ", " + rank);
}
}
}
In general things like SortedSet make more sense when you need to keep the data itself sorted. When you just need to process something in a sorted manner one time they're usually more trouble than they're worth. (Also: is there any reason why you're using a TreeMap? TreeMaps sort their keys, but not by value, so in this case you're not taking advantage of that sorting. Using a HashMap is more common in that case.)
You do a lot of work with the iterator, calling toString(), then splitting the results. And your Comparator is extra work too. Stay with a Map on both sides - you can use keys() and values() more directly, and let Java do the sorting for you. Most of your above code can be replaced with: (for clarity, I changed your name "mm" to "originalMap")
Map<Integer, String> inverseMap = new TreeMap<Integer, String>();
for (Map.Entry<String, Integer> entry : originalMap.entrySet()) {
inverseMap.put(entry.getValue(), entry.getKey());
}
Now, iterate over inverseMap to print the results. Note that if a count does exist twice in originalMap, only one will be printed, which is what you want. But which one gets printed left as an exercise for the reader :-). You might want to be more specific on that.
EDIT ADDED: If you do want to print out duplicate scores, this is not what you want. The original post I read said to skip if they were the same, but I don't see that after the edits, so I'm not sure if this is what OP wants.

Get top 10 values in hash map

I am trying to figure out how could I get the top 10 values from the HashMap. I was initially trying to use the TreeMap and have it sort by value and then take the first 10 values however it seems that that is not the option, as TreeMap sorts by key.
I want to still be able to know which keys have the highest values, the K, V of the map are String, Integer.
Maybe you should implement the Comparable Interface to your value objects stored in the hashmap.
Then you can create a array list of all values:
List<YourValueType> l = new ArrayList<YourValueType>(hashmap.values());
Collection.sort(l);
l = l.subList(0,10);
Regards
import java.util.Comparator;
import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;
public class Testing {
public static void main(String[] args) {
HashMap<String,Double> map = new HashMap<String,Double>();
ValueComparator bvc = new ValueComparator(map);
TreeMap<String,Double> sorted_map = new TreeMap<String,Double>(bvc);
map.put("A",99.5);
map.put("B",67.4);
map.put("C",67.4);
map.put("D",67.3);
System.out.println("unsorted map: "+map);
sorted_map.putAll(map);
System.out.println("results: "+sorted_map);
}
}
class ValueComparator implements Comparator<String> {
Map<String, Double> base;
public ValueComparator(Map<String, Double> base) {
this.base = base;
}
// Note: this comparator imposes orderings that are inconsistent with equals.
public int compare(String a, String b) {
if (base.get(a) >= base.get(b)) {
return -1;
} else {
return 1;
} // returning 0 would merge keys
}
}
I am afraid you'll have to iterate over the entire map. Heap is a commonly-used data structure for finding top K elements, as explained in this book.
If you are trying to get the 10 highest values of the map (assuming the values are numeric or at least implementing Comparable) then try this:
List list = new ArrayList(hashMap.values());
Collections.sort(list);
for(int i=0; i<10; i++) {
// Deal with your value
}
Let's assume you have a Map, but this example can work for any type of
Map<String, String> m = yourMethodToGetYourMap();
List<String> c = new ArrayList<String>(m.values());
Collections.sort(c);
for(int i=0 ; i< 10; ++i) {
System.out.println(i + " rank is " + c.get(i));
}
I base my answer in this one from sk2212
First you need to implement a descending comparator:
class EntryComparator implements Comparator<Entry<String,Integer>> {
/**
* Implements descending order.
*/
#Override
public int compare(Entry<String, Integer> o1, Entry<String, Integer> o2) {
if (o1.getValue() < o2.getValue()) {
return 1;
} else if (o1.getValue() > o2.getValue()) {
return -1;
}
return 0;
}
}
Then you can use it in a method such as this one for the attribute "hashmap":
public List<Entry<String,Integer>> getTopKeysWithOccurences(int top) {
List<Entry<String,Integer>> results = new ArrayList<>(hashmap.entrySet());
Collections.sort(results, new EntryComparator());
return results.subList(0, top);
}
public static void main(String[] args) {
HashMap<String, Integer> map = new HashMap<String, Integer>();
// Initialize map
System.out.println(getTopKeysWithOccurences(map, 10));
}
public static List<Entry<String,Integer>> getTopKeysWithOccurences(Map mp, int top) {
List<Entry<String,Double>> results = new ArrayList<>(mp.entrySet());
Collections.sort(results, (e1,e2) -> e2.getValue() - e1.getValue());
//Ascending order - e1.getValue() - e2.getValue()
//Descending order - e2.getValue() - e1.getValue()
return results.subList(0, top);
}

Arranging corresponding values in descending manner

I have two sets of columns right now but I am not sure how do I program it and what set of class for me to use it. Both are in separate arrays. One for letters and the other for the numbers.
a 12
b 9
c 156
So a corresponds to 12 and b corresponds to 9 etc etc. The list is actually a frequency of letters in a text file so I have 26 of them.
Both are not in the same array. As I have separate arrays for both of them. I want to try and arrange
and make them be in a descending manner.
So that the output would be:
c 156
a 12
b 9
I'm still unsure about various capabilities of ArrayList or HashMap or Tree Map. So any help with this?
import java.util.Comparator;
import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;
public class Test {
public static void main(String[] args) {
HashMap<String,Integer> map = new HashMap<String,Integer>();
ValueComparator bvc = new ValueComparator(map);
TreeMap<String,Integer> sorted_map = new TreeMap(bvc);
map.put("A",5);
map.put("B",60);
map.put("C",65);
map.put("D",3);
System.out.println("unsorted map");
for (String key : map.keySet()) {
System.out.println("key/value: " + key + "/"+map.get(key));
}
sorted_map.putAll(map);
System.out.println("results");
for (String key : sorted_map.keySet()) {
System.out.println("key/value: " + key + "/"+sorted_map.get(key));
}
}
}
class ValueComparator implements Comparator {
Map base;
public ValueComparator(Map base) {
this.base = base;
}
public int compare(Object a, Object b) {
if((Integer)base.get(a) < (Integer)base.get(b)) {
return 1;
} else if((Integer)base.get(a) == (Integer)base.get(b)) {
return 0;
} else {
return -1;
}
}
}
You can create a class that has the character and the frequency as data member, and make the class implements Comparable interface.
There are 2 options here, you can either:
Insert all objects into a class that implements List interface (e.g. ArrayList). Then call Collections.sort(List<T> list) on the List.
Insert all objects into TreeSet. You can obtain the sorted item via iterator().
From the question, it seems that the 2 pieces of data are not members of some existing object in the first place. If any chance that they do, you don't have to create a new class for the character and frequency. You can insert the existing object into a List, implement a class that extends on Comparator interface, and sort the List with Collections.sort(List<T> list, Comparator<? super T> c).
You have the following two lists.
[a, b, c]
[12, 9, 156]
First, zip the two lists together, so that you get a list (or a projection thereof) of tuples.
[(a, 12), (b, 9), (c, 156)]
Then sort this list by second item in each tuple, with whatever ordering you want.
[(c, 156), (a, 12), (b, 1)]
Now unzip this back into the two lists.
[c, a, b]
[156, 12, 1]
And there's your answer.
The italicized words indicate the generic abstractions used in the above solution, all of which are likely already available in this library.
Try this:
public static void main(String[] args) throws Exception {
char[] letters = { 'a', 'b', 'c' };
int[] numbers = { 12, 9, 156 };
printDescendind(letters, numbers);
}
public static void printDescendind(char[] letters, int[] numbers) {
class Holder implements Comparable<Holder> {
char letter;
int number;
public int compareTo(Holder o) {
return number != o.number ? o.number - number : letter - o.letter;
}
public String toString() {
return String.format("%s %s", letter, number);
}
}
List<Holder> list = new ArrayList<Holder>(letters.length);
for (int i = 0; i < letters.length; i++) {
Holder h = new Holder();
h.letter = letters[i];
h.number = numbers[i];
list.add(h);
}
Collections.sort(list);
for (Holder holder : list) {
System.out.println(holder);
}
}

Categories