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.
Related
I'm trying to sort a HashMap<String, Long>. I'm have the following code for sorting:
private static class ValueComparator implements Comparator<String>{
HashMap<String, Long> map = new HashMap<String, Long>();
public ValueComparator(HashMap<String, Long> map){
this.map.putAll(map);
}
#Override
public int compare(String s1, String s2) {
if(map.get(s1) > map.get(s2)){
System.out.println("s1: " + s1 + "; s2: " + s2);
return -1;
}
else if (map.get(s1).equals(map.get(s2))) {
return 0;
}
else{
return 1;
}
}
}
private static TreeMap<String, Long> sortMapByValue(HashMap<String, Long> map){
Comparator<String> comparator = new ValueComparator(map);
//TreeMap is a map sorted by its keys.
//The comparator is used to sort the TreeMap by keys.
TreeMap<String, Long> result = new TreeMap<String, Long>(comparator);
result.putAll(map);
System.out.println("DONE sort");
return result;
}
The problem is, when several different keys have the same values, only one of the key makes it into the final map:
EXAMPLE:
public class Test {
public static void main(String[] args) {
HashMap<String, Long> hashMap = new HashMap<>();
hashMap.put("Cat", (long) 4);
hashMap.put("Human", (long) 2);
hashMap.put("Dog", (long) 4);
hashMap.put("Fish", (long) 0);
hashMap.put("Tree", (long) 1);
hashMap.put("Three-legged-human", (long) 3);
hashMap.put("Monkey", (long) 2);
System.out.println(hashMap); //7 pairs
System.out.println(sortMapByValue(hashMap)); //5 pairs
}
}
How would I fix it?
I don't think it's fixable you are using the the maps in an unintended way and breaking contracts. Tree map is expecting to be sorted by the key and the key is expected to be unique so when the compare == 0 it will just override the node's value. You can always implement your own TreeMap and make it do whatever you want it to.
I'm not sure what you want to do with it but I think you need something like
TreeMap<Long,List<String>>
http://hg.openjdk.java.net/jdk8/jdk8/jdk/file/687fd7c7986d/src/share/classes/java/util/TreeMap.java
if (cpr != null) {
do {
parent = t;
cmp = cpr.compare(key, t.key);
if (cmp < 0)
t = t.left;
else if (cmp > 0)
t = t.right;
else
return t.setValue(value);
} while (t != null);
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.
I have a map of string keys and int values, I to sort them, and keep them sorted when I change values.
I tried using a treemap for the sorted pairs and a normal map for the unsorted paris so I can use it in the comparator, but after a one value passes the other I get a null expection, this is the defenition:
public static TreeMap<String, Long> countryData;
public static ValueComparator bvc;
public static void setCountryData(HashMap<String, Long> map){
bvc = new ValueComparator(map);
countryData = new TreeMap<String, Long>(bvc);
countryData.putAll(map);
System.out.println(Arrays.toString(countyNames));
System.out.println(countryData.values());
}
public static class ValueComparator implements Comparator<String> {
Map<String, Long> base;
public ValueComparator(Map<String, Long> base) {
this.base = base;
}
public int compare(String a, String b) {
if(base.get(a).equals(base.get(b))){
return 0;
}
if (base.get(a) > base.get(b)) {
return -1;
} else {
return 1;
}
}
}
this is how I change the values:
General.bvc.base.put(country, newValue);
General.countryData.put(country, newValue);
after one value passes another and I try to acces it, I get a null, how can I Do this?
How can I have a TreeMap which is sorted by value ..
You can't. They are sorted by key.
...
Irrelevant.
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);
}
I have been wondering for a few days now, what could be the least messy approach to my problem. I have a set of 10 enum types e.g. { ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE } and I use these enums as keys in a map
Map<MyEnumType, Integer> myMap;
Then I generate 100 or so of these maps, with the same keys but different values. What is the best practice for adding all of these maps into one? I mean adding up all the value of those 100 maps I have generated.
Iterate over the enum values, and for each enum value, iterate over the maps (or vice-versa):
Map<MyEnumType, Integer> sumMap = new EnumMap<MyEnumType, Integer>(MyEnumType.class);
for (MyEnumType e : MyEnumType.values()) {
int sum = 0;
for (Map<MyEnumType, Integer> map : maps) {
sum += map.get(e); // you might want a null check here
}
sumMap.put(e, sum);
}
Other option would be to create class specific for summing integers instead of overriding previous values. Here's an examples how it's done with anonymous class:
public class MapSummer {
private final Map<MyEnumType, Integer> sumMap = new HashMap<MyEnumType, Integer>() {
#Override
public Integer put(MyEnumType key, Integer value) {
return super.put(key, get(key) + value);
}
#Override
public Integer get(Object key) {
return super.get(key) != null ? super.get(key) : 0;
}
};
public Map<MyEnumType, Integer> sum(List<Map<MyEnumType, Integer>> mapList) {
for (Map<MyEnumType, Integer> map : mapList) {
sumMap.putAll(map);
}
return sumMap;
}
}
enum MyEnumType {
ONE, TWO, THREE, FOUR;
}
And unit test:
public class MapSummerTest {
private final MapSummer summer = new MapSummer();
#Test
public void shouldSumValuesInMap() {
final Map<MyEnumType, Integer> map1 = new HashMap<MyEnumType, Integer>() {{
put(ONE, 1);
put(TWO, 2);
}};
final Map<MyEnumType, Integer> map2 = new HashMap<MyEnumType, Integer>() {{
put(TWO, 2);
put(THREE, 3);
}};
final Map<MyEnumType, Integer> sumMap = summer.sum(Arrays.asList(map1, map2));
assertThat(sumMap.get(ONE), equalTo(1));
assertThat(sumMap.get(TWO), equalTo(4));
assertThat(sumMap.get(THREE), equalTo(3));
assertThat(sumMap.get(FOUR), equalTo(0));
}
}