I have a problem displaying the data right. I have an sql query that returns the following data:
| count (long) | country (string)| ---------------| ------------------ | 6 | null | | 3 | spain | | 6 | italy |
And I have the following method:
private Map<String, Long> countryCount (List<CountryCount> summary) { Map<String, Long> result = new HashMap<>(); summary.forEach(sum -> { var country= sum.getCountry(); if (country == null) { country= "unknown"; } var count = result.get(country); if (count == null) { count = 0L; } result.put(country, count + sum.getCount()); }); return result.entrySet().stream().sorted(Map.Entry.comparingByKey()) .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (oldValue, newValue) -> oldValue, LinkedHashMap::new)); }
In my method I want to return an object like {"unknown" : 5, "spain" : 3, "italy" : 6}
but instead I get {"unknown" : 15}
Advertisement
Answer
Perhaps this simpler code.
Map < String, Long > map = listOfCountryCount .stream() .collect( Collectors.toMap( countryCount -> Objects.isNull( countryCount.getCountry() ) ? "unknown" : countryCount.getCountry() , countryCount -> Objects.isNull( countryCount.getCount() ) ? Long.valueOf( 0 ) : countryCount.getCount() ) );
If you want a sorted map, pass that map to constructor of a SortedMap
or NavigableMap
implementation.
NavigableMap< String , Long > mapNav = new TreeMap<>( map ) ;
Full example code.
package work.basil.demo; import java.util.*; import java.util.stream.Collectors; class Ideone { public static void main ( String[] args ) throws java.lang.Exception { List < CountryCount > listOfCountryCount = List.of( new CountryCount( "Peru" , 17L ) , new CountryCount( "Xanadu" , 128L ) , new CountryCount( null , 42L ) , new CountryCount( "Andorra" , null ) ); Map < String, Long > map = listOfCountryCount .stream() .collect( Collectors.toMap( countryCount -> Objects.isNull( countryCount.getCountry() ) ? "unknown" : countryCount.getCountry() , countryCount -> Objects.isNull( countryCount.getCount() ) ? Long.valueOf( 0 ) : countryCount.getCount() ) ); NavigableMap < String, Long > mapNav = new TreeMap <>( map ); // Keys are maintained in sorted order. System.out.println( "listOfCountryCount = " + listOfCountryCount ); System.out.println( "map = " + map ); System.out.println( "mapNav = " + mapNav ); } } final class CountryCount { private final String country; private final Long count; CountryCount ( String country , Long count ) { this.country = country; this.count = count; } public String getCountry ( ) { return country; } public Long getCount ( ) { return count; } @Override public boolean equals ( Object obj ) { if ( obj == this ) return true; if ( obj == null || obj.getClass() != this.getClass() ) return false; var that = ( CountryCount ) obj; return Objects.equals( this.country , that.country ) && Objects.equals( this.count , that.count ); } @Override public int hashCode ( ) { return Objects.hash( country , count ); } @Override public String toString ( ) { return "CountryCount[" + "country=" + country + ", " + "count=" + count + ']'; } }
When run.
listOfCountryCount = [CountryCount[country=Peru, count=17], CountryCount[country=Xanadu, count=128], CountryCount[country=null, count=42], CountryCount[country=Andorra, count=null]] map = {Andorra=0, Xanadu=128, Peru=17, unknown=42} mapNav = {Andorra=0, Peru=17, Xanadu=128, unknown=42}
If you are not sure about your data being distinct, let’s add a third argument to that Collectors.toMap
call. We pass a lambda function to be called in case of duplicate keys. In our case here, we go with a policy of first occurrence wins.
Map < String, Long > map = listOfCountryCount .stream() .collect( Collectors.toMap( countryCount -> Objects.isNull( countryCount.getCountry() ) ? "unknown" : countryCount.getCountry() , countryCount -> Objects.isNull( countryCount.getCount() ) ? Long.valueOf( 0 ) : countryCount.getCount() , ( existing , replacement ) -> existing // In case of duplicate key conflict, first one wins. ) );
We can shorten this code further. Rather than create a TreeMap
separately, we can pass a constructor method reference as a fourth argument to our Collectors.toMap
: TreeMap :: new
. And we change the declaration of the returned map from Map
to NavigableMap
.
NavigableMap < String, Long > map = listOfCountryCount .stream() .collect( Collectors.toMap( countryCount -> Objects.isNull( countryCount.getCountry() ) ? "unknown" : countryCount.getCountry() , countryCount -> Objects.isNull( countryCount.getCount() ) ? Long.valueOf( 0 ) : countryCount.getCount() , ( existing , replacement ) -> existing , // In case of duplicate key conflict, first one wins. TreeMap :: new ) );
See this code run live at IdeOne.com.
listOfCountryCount = [CountryCount[country=Peru, count=17], CountryCount[country=Xanadu, count=128], CountryCount[country=null, count=42], CountryCount[country=Andorra, count=null]] map = {Andorra=0, Peru=17, Xanadu=128, unknown=42}