Home > Software engineering >  Print the contents of a HashMap in sorted order according to the Size of each HashMap nested inside
Print the contents of a HashMap in sorted order according to the Size of each HashMap nested inside

Time:06-27

I have a HashMap inside a HashMap. I want to print the elements in the HashMap according to the size of elements in the inner HashMap. So the element with the highest number of elements should print first. I'm new to HashMaps and got stuck.

This is the HashMap and how I'm printing it:

Map<String, Map<String, Integer>> states = new ConcurrentHashMap<String, Map<String, Integer>>();

for(Entry<String, Map<String, Integer>> entry : states.entrySet()) {

    System.out.println("State:"   entry.getKey());

    Map<String, Integer> tempMap = entry.getValue();

    for(Entry<String, Integer> innerEntry : tempMap.entrySet()) {
        System.out.println("City:"   innerEntry.getKey()   " Count:"   innerEntry.getValue());
    }
    
    System.out.println();
}

The output that I'm currently getting:

State:Texas
City:Austin Count:1

State:Hawaii
City:Honolulu Count:1
City:Kihei Count:1
City:Maui Count:1

State:California
City:Newport Beach Count:1

The output I need:

State:Hawaii
City:Honolulu Count:1
City:Kihei Count:1
City:Maui Count:1

State:Texas
City:Austin Count:1

State:california
City:Newport Beach Count:1

CodePudding user response:

HashMap is not capable of maintaining the order, therefore you can't sort it.

You can dump the map contents into a list and sort it using a custom comparator. But there's something that you're missing - the way you're structuring the data is wrong, state and cities that belong to that state are two pieces of information are closely related to each other. Using a map to combine them together is an abuse of collections, it makes your code rigid and unmaintainable.

Use the power of Objects

Your code doesn't become object-oriented automatically because of the fact that you're using an object-oriented language.

The correct approach would be to define State and City as classes and maintain a list of State objects instead of dealing with a nested map.

class State {
    private String name;
    private List<City> cities = new ArrayList<>();
    
    public State(String name) {
        this.name = name;
    }
    
    public void addCity(City city) {
        cities.add(city);
    }
    
    public boolean removeCity(City city) {
        return cities.remove(city);
    }
    
    public List<City> getCities() {
        return cities;
    }
    
    @Override
    public String toString() {
        return "State:"   name   "\n"  
            cities.stream()
                .map(City::toString)
                .collect(Collectors.joining("\n"))
              "\n";
    }
}

class City {
    private String name;
    private int count;
    
    public City(String name, int count) {
        this.name = name;
        this.count = count;
    }
    
    @Override
    public String toString() {
        return "City:"   name   " Count:"   count;
    }
}

That's how you can use this classes in the client code:

public static void main(String[] args) {
    List<State> states = new ArrayList<>();
    State texas = new State("Texas");
    texas.addCity(new City("Austin", 1));
    
    State hawaii = new State("Hawaii");
    hawaii.addCity(new City("Honolulu", 1));
    hawaii.addCity(new City("Kihei", 1));
    hawaii.addCity(new City("Maui", 1));
    
    State california = new State("California");
    california.addCity(new City("Newport Beach", 1));
    
    Collections.addAll(states, texas, hawaii, california);
    
    states.sort(Comparator.<State>comparingInt(state -> state.getCities().size()).reversed());
    
    for (State state: states) {
        System.out.println(state);
    }
}

Output:

State:Hawaii
City:Honolulu Count:1
City:Kihei Count:1
City:Maui Count:1

State:Texas
City:Austin Count:1

State:California
City:Newport Beach Count:1

CodePudding user response:

Since ConcurrentHashMap makes no guarantees about ordering, you have to sort your map be getting entrySet and sort them then re-put into a LinkedHashMap

Map<String, Map<String, Integer>> sortedStates = new LinkedHashMap<String, Map<String, Integer>>();
states.entrySet().stream()
        .sorted(new Comparator<Map.Entry<String, Map<String, Integer>>>() {
            public int compare(Map.Entry<String, Map<String, Integer>> mapA, Map.Entry<String, Map<String, Integer>> mapB){
                return mapB.getValue().size() - mapA.getValue().size();
              }
            })
        .forEachOrdered(mapInner -> sortedStates.put(mapInner.getKey(), mapInner.getValue()));

CodePudding user response:

  1. create a size_map -> Map<Integer, List> size_map = new HashMap<Integer, List>();
  • This is to store store number of cities as key and states as the value. Note here states are stored in a list because multiple states can have same number of cities.
  1. Make a list (size_list) out of keys of map in point 1. (Keys are number of cities for different states). Order this list in descending order

  2. While iterating your "states" Hashmap, use size_list as a lookup and fetch the list of states with the current size from size_map HashMap. Basically use size_list as a lookup.

Note - this will increase your overhead and performance might not be great.

    Map<String, Map<String, Integer>> states = new ConcurrentHashMap<String, Map<String, Integer>>();

    //create size HashMap -- store number of cities as key and value would be list of states 
    Map<Integer, List<String>> size_map = new HashMap<Integer, List<String>>();

    for(Map.Entry<String, Map<String, Integer>> entry : states.entrySet())
    {
        int size = entry.getValue().size();
        String state = entry.getKey();
        if(size_map.containsKey(size))
        {
            List<String> retreived_list = size_map.get(size);
            retreived_list.add(state);
            size_map.put(size, retreived_list);
        }
        else
        {
            List<String> retreived_list = new ArrayList<String>();
            retreived_list.add(state);
            size_map.put(size, retreived_list);
        }
    }

    //create a list out of size_map keys and sort it in descending order
    List<Integer> size_list = new ArrayList<Integer>(size_map.keySet());
    Collections.sort(size_list, Collections.reverseOrder());

    //index will be current index in size_list, fetch states list from size_map and look for the same state in the original "state" hashmap
    int index =0;
    for(Map.Entry<String, Map<String, Integer>> entry : states.entrySet())
    {
        int current_size = size_list.get(index);
        List<String> states_list = size_map.get(current_size);

        for(String s : states_list)
        {
            System.out.println("State:"   s);
            Map<String, Integer> tempMap = states.get(s);
            for(Map.Entry<String, Integer> innerEntry : tempMap.entrySet()) {
                System.out.println("City:"   innerEntry.getKey()   " Count:"   innerEntry.getValue());
            }
        }
        index  ;
        System.out.println();
    }

CodePudding user response:

You can use Streams API for sorting before print it out:

Comparator<Entry<String, Map<String, Integer>>> entryComparator =
        Comparator.comparingInt((Entry<String, Map<String, Integer>> entryOne) -> entryOne.getValue().size());

    for(Entry<String, Map<String, Integer>> entry : states.entrySet().stream().sorted(entryComparator.reversed()).collect(
        Collectors.toList())) {
      System.out.println("State:"   entry.getKey());
      Map<String, Integer> tempMap = entry.getValue();
      for(Entry<String, Integer> innerEntry : tempMap.entrySet()) {
        System.out.println("City:"   innerEntry.getKey()   " Count:"   innerEntry.getValue());
      }

      System.out.println();
    }

CodePudding user response:

HashMap makes no guarantees as to the order of the map; in particular, it does not guarantee that the order will remain constant over time. Using LinkedHashMap instead and that will look like:

Map sortedMap = new LinkedHashMap();
originalHashMap.entrySet().stream()
     .sorted(Comparator.comparingInt(e -> e.getValue().size()))
     .forEachOrdered(e -> sortedMap.put(e.getKey(), e.getValue()));

Now, expected sorted map is sortedMap

CodePudding user response:

Try this.

Map<String, Map<String, Integer>> states = new ConcurrentHashMap<String, Map<String, Integer>>();
states.put("Texas", Map.of("Austin", 1));
states.put("Hawaii", Map.of("Honolulu", 1, "Kihei", 1, "Maui", 1));
states.put("California", Map.of("Newport Beach", 1)); 

states.entrySet().stream()
    .sorted(Comparator.comparing(e -> -e.getValue().size()))
    .forEach(e -> {
        System.out.println("State:"   e.getKey());
        e.getValue().entrySet().stream()
            .forEach(f -> System.out.println("City:"   f.getKey()   " Count:"   f.getValue()));
        System.out.println();
    });

output:

State:Hawaii
City:Maui Count:1
City:Honolulu Count:1
City:Kihei Count:1

State:Texas
City:Austin Count:1

State:California
City:Newport Beach Count:1
  • Related