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));
}
}
Related
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.
This question already has answers here:
TreeMap sort by value
(13 answers)
Closed 7 years ago.
This is the code to sort HashMap by Value.
public class LongestChain
{
public static void main(String[] args)
{
String words[] = new String[]{"a","b", "c", "ca","ba","bca","bda","bdca"};
System.out.println(longestChainOfWords(words));
}
public static int longestChainOfWords(String words[])
{
Map<String, Integer> map = new HashMap<String, Integer>();
for(String a:words)
{
map.put(a, a.length());
}
System.out.println(map);
Map sortedMap = sortByValue(map);
System.out.println(sortedMap);
return sortedMap.size();
}
public static Map<String, Integer> sortByValue(Map<String, Integer> unsortedMap)
{
Map<String, Integer> sortedMap = new TreeMap<String, Integer>(new ValueComparator(unsortedMap));
sortedMap.putAll(unsortedMap);
return sortedMap;
}
}
class ValueComparator implements Comparator
{
Map<String, Integer> map;
public ValueComparator(Map<String, Integer> map)
{
this.map = map;
}
public int compare(Object keyA, Object keyB)
{
Comparable valueA = map.get(keyA);
Comparable valueB = map.get(keyB);
System.out.println(keyA+" keyA "); System.out.println(keyB+" keyB ");
return valueA.compareTo(valueB);
}
}
Output is like this. I was expecting 8 elements in sortedMap too. Why the behavior is like this?
{ca=2, bda=3, ba=2, b=1, c=1, a=1, bdca=4, bca=3}
{b=1, ca=2, bda=3, bdca=4}
4
Because you let the TreeMap think it sorts keys. And if the key is equal the no new value will be put inside. As you have 4 different values, so you can find 4 results in your list.
What you can do is improve the compare - method so equal values will be ordered by their keys:
class ValueComparator implements Comparator<String> {
Map<String, Integer> map;
public ValueComparator(final Map<String, Integer> map) {
this.map = map;
}
#Override
public int compare(final String keyA, final String keyB) {
final Integer valueA = this.map.get(keyA);
final Integer valueB = this.map.get(keyB);
System.out.println(keyA + " keyA ");
System.out.println(keyB + " keyB ");
final int compared = valueA.compareTo(valueB);
if (compared != 0) {
return compared;
} else {
return keyA.compareTo(keyB);
}
}
}
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?
I created a map and then sort the values and put them in a TreeMap. But the problem is when I invoke the remove() method on Treemap, the element is not removed.
Does anybody have any idea what the problem is?
Here is my code:
Map<String , Double> map=new HashMap<String, Double>();
TreeMap<String, Double> sortedItems = sortMap(map);
sortedItems.remove("I put the key here as a string");
public TreeMap<String, Double> sortMap(Map<String, Double> map) {
HashMap<String, Double> map2 = new HashMap<String, Double>(map);
TreeMap<String, Double> sortedMap = SortByValue(map2);
return sortedMap;
}
public TreeMap<String, Double> SortByValue
(HashMap<String, Double> map) {
ValueComparator vc = new ValueComparator(map);
TreeMap<String, Double> sortedMap = new TreeMap<String, Double>(vc);
sortedMap.putAll(map);
return sortedMap;
}
class ValueComparator implements Comparator<String> {
Map<String, Double> map;
public ValueComparator(Map<String, Double> base) {
this.map = base;
}
public int compare(String a, String b) {
if (map.get(a) >= map.get(b)) {
return -1;
} else {
return 1;
} // returning 0 would merge keys
}
You need to return 0; when you expect a match. The way to solve this is to compare the key which it is otherwise a match. This way the same key will match, but only the same key.
public int compare(String a, String b) {
int cmp = -map.get(a).compareTo(map.get(b));
if (cmp == 0)
cmp = a.compareTo(b);
return cmp;
}
This way String keys which map to the same Double are seen as different iff they are different Strings.
returning 0 would merge keys
Returning 0 means the elements are equal. If you don't return 0, elements are never equal. Your TreeMap uses the given Comparator to put and remove. Your Comparator never identifies two keys as equal and therefore cannot remove anything.
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.