Home > Software engineering >  Compare keys in a Map with elements in a List in java
Compare keys in a Map with elements in a List in java

Time:05-08

I have a static HashMap <Integer, String> and an Arraylist<Integer>.

public static final HashMap<Integer, String> CONSTANT_MAP =
    (HashMap<Integer, String>) Collections.unmodifiableMap(new HashMap<Integer, String>() {
        {
            put(28, "Action");
            put(12, "Adventure");
            put(16, "Animation");
            put(35, "Comedy");
            put(80, "Crime");
            put(99, "Documentary");
            put(18, "Drama");
            put(10751, "Family");
            put(14, "Fantasy");
            put(36, "History");
            put(27, "Horror");
            put(10402, "Music");
            put(9648, "Mystery");
            put(10749, "Romance");
            put(878, "Science Fiction");
            put(10770, "TV Movie");
            put(53, "Thriller");
            put(10752, "War");
            put(37, "Western");
        }
    });

I need a method that would compare HashMap keys with ArrayList elements. If the keys are equal, the Hashmap value should be put to a separate ArrayList and be returned.

I tried the following, but it doesn't work. I get ExceptionInInitializerError.

public static ArrayList<String> getGender(ArrayList<Integer> arrayList, HashMap<Integer, String> hashMap) {
    ArrayList<String> resultList = new ArrayList<String>();
    for (Map.Entry m : hashMap.entrySet()) {
        if (arrayList.contains(m.getValue()))
            resultList = (ArrayList<String>) m.getValue();
    }
    return resultList;
} 

CodePudding user response:

I get ExceptionInInitializerError

This error is triggered by an exception that occur during initialization of a static field or while executing a static initializer block. A quote from the Javadoc:

An ExceptionInInitializerError is thrown to indicate that an exception occurred during evaluation of a static initializer or the initializer for a static variable.

In this case, you are trying to typecast a map returned by unmodifiableMap() to HashMap and this casting fails. It can't succeed because unmodifiableMap() is meant to wrap any implementation of the Map interface (TreeMap, LinkedHashMap, etc.). And this method returns an instance of class UnmodifiableMap which implements Map interface (i.e. it overrides all required methods) and maintains a variable of type Map (a wrapped map that was passed as an argument) and delegates to calls to it. Inherently UnmodifiableMap and HashMap are siblings, they both implement Map, but they don't extend each other, therefore you can't cast between these types.

A remedy: write your code against interfaces, not concrete types, and you'll be fine. CONSTANT_MAP should be of type Map, not HashMap (by the way, I suggest to use more meaningful name like ganreById). Similarly, an argument of your method should be a List, not ArrayList.

I need a method that would compare HashMap keys with ArrayList elements. If the keys are equal the Hashmap value should be put to a separate ArrayList and be returned.

Firstly, judging by the contents of the map I believe you've misspelled the method name, and you meant genre, not gender. In the code below I've changed it, as well as parameter-names (sure, names are totally up to you, it's only an example, but keep in mind that they have a huge impact on the readability of the code and hence important) and parameter types are changed to be the types of interfaces List and Map.

The most efficient way to approach this task is to iterate over the list and check whether a particular element is contained in the map.

Not the opposite like you've done in your code because contains() check is costful on a list (you need to walk through all the elements to find out if the matching element exists), it's O(n) time if you're familiar with Big O notation. Conversely, containsKey() check on a HashMap is almost instant - O(1).

If the matching key exist, the value associated with it will get added to the resulting list.

public static List<String> getGenre(List<Integer> ids, 
                                    Map<Integer, String> genreById) {

    List<String> result = new ArrayList<>();
    for (Integer id: ids) {
        if (genreById.containsKey(id))
            result.add(genreById.get(id));
    }
    return result;
}

Sidenotes:

  • If this method resides in the same class where the map is defined as a static field, passing it as parameter is redundant.
  • You can substitute double-curly braces initialization (which isn't considered to be a good practice) with the static method Map.ofEntries() accessible with Java 9. It returns a map which is already unmodifiable, so there will be no need to wrap it with Collections.unmodifiableMap().

CodePudding user response:

well, if you just want to check whether the keys in hashmap is equal to the arraylist, you could use hashMap.keySet().equals(new HashSet<Integer>(arrayList)), and then use hashMap.values() to get all the values.

CodePudding user response:

The answer from Alexander Ivanchenko correctly identifies the cause of the exception you're seeing (so you should accept his answer!). My answer talks below talks about other problems with your code.


Your hashmap contains values that are Strings:

    HashMap<Integer, String> hashMap

therefore

    m.getValue()

is a String, and therefore you cannot cast it to an ArrayList of Strings

    resultList = (ArrayList<String>) m.getValue();

I think what you want is to accumulate values in your result list:

   resultList.add(m.getValue());

Additionally, as pointed out by @racraman, this comparison is problematic:

   if (arrayList.contains(m.getValue())

The list arrayList contains Integers; m.getValue() is a String; I am surprised this compiles at all. I think from the description you meant

    if (arrayList.contains(m.getKey())

so maybe was a transcription error when writing this post?

CodePudding user response:

Long story short, use the collections library to do the work for you:

public static List<String> getGenres(List<Integer> list, Map<Integer, String> map) {
    return map.entrySet().stream()
        .filter(e -> list.contains(e.getKey()))
        .map(Map.Entry::getValue)
        .collect(Collectors.toList());
}

Note also the use of the abstract type (eg List, Map, etc) in preference to the concrete type (eg ArrayList, HashMap, etc): see The Liskov substitution principle (LSP)

Note also the correction of the spelling error in the method name from getGender to getGenres, correcting both the semantic meaning and pluralisation - methods that return a collection should be named in the plural.

  • Related