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:
- 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.
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
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