How to get the two highest values ​​in a HashMap <Integer, String> while doing a rating

I have the following code:

public class Tester {

    public static void main(String[] args) {
        HashMap<Integer,String> map = new HashMap<Integer,String>();

        map.put(1, "one");
        map.put(2, "twp");
        map.put(2, "two2");

        int highest = Integer.MIN_VALUE;
        String highestString = null;
        int secondHighest = Integer.MIN_VALUE;
        String secondHighestString = null;

        if (highest == Integer.MIN_VALUE){
            highest = Collections.max(map.keySet() );
            highestString = map.get(highest);
            map.remove(highest);
        }
        if (secondHighest == Integer.MIN_VALUE ){
            secondHighest = Collections.max(map.keySet() );
            secondHighestString = map.get(secondHighest);
            map.remove(secondHighest);
        }

        System.out.println(highest + highestString);
        System.out.println(secondHighest + secondHighestString);


    }

}

      

I can't get both of the highest values ​​back as I tried, because it seems impossible to delete only one value with the same key, and I also tried swapping them (making HashMap not the best option either). Should I try to use any other collection?

I've also tried:

TreeSet<Tete> set = new TreeSet<Tete>();

    set.add(new Tete("name1", 1));
    set.add(new Tete("name2",4));
    set.add(new Tete("name3",4));
    set.add(new Tete("name4",12));

    System.out.println(set.size());

      

Assuming the "Tete" class contains only one string and one integer, the size of the set is only 3, not 4 as expected. And if I print all the numbers, "name3" is not printed, so I cannot return the 3 largest values, for example, only "name4", "name2" and "name1" will appear, but "name3" is greater than "name1" "

+3


source to share


2 answers


To solve the ranking problem, I would write a custom class something like this:

public class TopsCollection<D> {

    private final TreeMap<Integer, List<D>> map 
        = new TreeMap<>((lhv, rhv) -> rhv.compareTo(lhv));

    public TopsCollection() {}

    public void add(Integer score, D name) {
        List<D> vals = map.get(score);
        if (vals == null) {
            vals = new ArrayList<>();
            map.put(score, vals);
        }
        vals.add(name);
    }

    public List<D> getTops(int n) {
        return map.
            values().
            stream().
            limit(n).
            reduce(new ArrayList<D>(), (lhv, rhv) -> {
                lhv.addAll(rhv);
                return lhv;
            });
    }
}

      

Using:

TopsCollection<String> tc = new TopsCollection<>();
tc.add(12, "nome4");
tc.add(1, "nome1");
tc.add(4, "nome3");
tc.add(4, "nome2");

List<String> tops = tc.getTops(2); // contains 3 elements: nome4, nome3, nome2

      



Notes:

This particular implementation can return any number of highest ranked records.

It should also be noted that the method add(...)

takes proportional time log(n)

because TopsCollection is supported by the class TreeMap

.

TopCollection

Can optionally implement an interface Collection<T>

to act like a real collection.

+1


source


Anyone Map

(including HashMap

) can only store one value for any key. Thus, after three calls map.put

, your map will only have two elements: [1,"Um"]

and [2,"dois2"]

. The value "dois"

will no longer appear on the map.

If you really need to store multiple values ​​for each key, the way to do it with the Java runtime is to map each keymap to a list of values.

HashMap<Integer,ArrayList<String>> map = new HashMap<Integer,ArrayList<String>>();

      

This means that the code for adding a value to a map and for removing a value is more complex. Adding a value requires the following code:



ArrayList<String> list = map.get(key);
if (list == null) {
    list = new ArrayList<String>();
    map.put(key, list);
}
list.add(newValue);

      

Deleting a value is similar: you need to get the list, then remove the value from ArrayList

and remove the key from the map, only if the size is ArrayList

now 0.

Alternatives: There are multi-map collections in third party libraries like Apache Commons ( javadoc ). Or, as @Keenle suggested, you can use a set where the key is an object containing both an integer and a string. You will have to write a class for this object, but that would be very simple.

+2


source







All Articles