相关文章推荐
Collectives™ on Stack Overflow

Find centralized, trusted content and collaborate around the technologies you use most.

Learn more about Collectives

Teams

Q&A for work

Connect and share knowledge within a single location that is structured and easy to search.

Learn more about Teams

I have one bean class Hotel in which data members are HotelName , HotelCity , HotelContact .

Now I want to save that data in Hashmap in a way where map Key should be City and all the hotel names should be which belongs to city should be in map values. I am able to do that using Collectors.groupby() . However I am not able to get the HotelName, instead I am getting Hotel object address. Please help where I am mistaking. Here is code which I attempted:

class Hotel {
    private String hotelName;
    private String hotelCity;
    private String hotelPhNo;
    /*their getter setter methods*/
public static void main(String[] args) {
    List<Hotel> ol = new ArrayList<Hotel>();
    Hotel h1 = new Hotel("H1","Pune","123");
    Hotel h2 = new Hotel("H2","Mumbai","109");
    Hotel h3 = new Hotel("H3","Pune","1123");
    Hotel h4 = new Hotel("H4","Mumbai","123");
    Hotel h5 = new Hotel("H5","Ujjain","123");
    Hotel h6 = new Hotel("H6","Indore","123");  
    Hotel h7 = new Hotel("H7","Sehore","123");  
    Hotel h8 = new Hotel("H8","Pune","123");    
    ol.add(h1);
    ol.add(h2);
    ol.add(h3);
    ol.add(h4);
    ol.add(h5);
    ol.add(h6);
    ol.add(h7);
    ol.add(h8);
    Map<String, List<Hotel>> res = ol.stream().collect(Collectors.groupingBy(Hotel:: getHotelCity));
    res.forEach((a,b)->System.out.println("City:"+a+" with Hotels: "+b));

With this I am getting output like:

City:Ujjain with Hotels: [Hotel@7ba4f24f]
City:Pune with Hotels: [Hotel@3b9a45b3, Hotel@7699a589, Hotel@58372a00]
City:Indore with Hotels: [Hotel@4dd8dc3]
City:Mumbai with Hotels: [Hotel@6d03e736, Hotel@568db2f2]
City:Sehore with Hotels: [Hotel@378bf509]

Desired output I want:

City:Ujjain with Hotels: [H5]
City:Pune with Hotels: [H1, H3, H8]
City:Indore with Hotels: [H6]
City:Mumbai with Hotels: [H4]
City:Sehore with Hotels: [H7]

Please help. I have a requirement where I don't want to alter bean class by overriding equals, hashcode and tostring method. Overriding these method would have been simpler to resolve this issue but looking for alternatives.

Alternatively to all the below, could you just update the toString method of your Hotel class to print the hotelName? – BeUndead Feb 19, 2021 at 16:05 @BeUndead I already mentioned in question itself that overriding these methods would work but I don't want to alter bean class. – gaurav kulkarni Feb 19, 2021 at 16:12 Technically you said 'overriding equals, hashcode and tostring', I'm suggesting editing just one of those. But fair enough if that's not for you. :) – BeUndead Feb 19, 2021 at 16:17

For such output, you need Map<String, List<String>> data strcture. To achieve that, use Collectors.mapping downstream collector within the Collectors.groupingBy.

Map<String, List<String>> res = ol.stream()
        .collect(Collectors.groupingBy(Hotel:: getHotelCity, 
                Collectors.mapping(Hotel::getHotelName, Collectors.toList())));

Note you need to provide additional downstream collector Collectors.toList() as long as Collectors.mapping needs it everytime.

The result:

res.forEach((a,b)->System.out.println("City:"+a+" with Hotels: "+b));
City:Ujjain with Hotels: [H5]
City:Pune with Hotels: [H1, H3, H8]
City:Indore with Hotels: [H6]
City:Mumbai with Hotels: [H2, H4]
City:Sehore with Hotels: [H7]

If you still want to keep the original Map<String, List<Hotel>> structure BUT print out the map values in a different way, you need to change the way you print out the result:

res.forEach((city, list) -> {
     String hotels = list.stream()
                         .map(Hotel::getHotelName)
                         .collect(Collectors.joining(", ", "[", "]"));
     System.out.println("City:" + city + " with Hotels: " + hotels);

Finally, few notes:

  • Name the class fields with a lower-case first letter: private String hotelName;

  • A List can be created in a shorter way:

    List<Hotel> ol = new ArrayList<>(Arrays.asList(h1, h2, h3, h4, h5, h6, h7, h8));
                    Thanks for your reply but any possibility with "Map<String, List<Hotel>> res" would be great.
    – gaurav kulkarni
                    Feb 19, 2021 at 15:54
    

    Problem here is b stands for List<Hotel>. To print the hotel names you should traverse the list b with b.forEach(...) then print each hotel information individually.

    You can change the print statement like this:

    res.forEach((a, b) -> {
        System.out.println("City:" + a + " with Hotels: ");
        b.forEach(h -> System.out.println("\t" + h.getHotelName()));
    

    Output:

    City:Ujjain with Hotels: 
    City:Pune with Hotels: 
    City:Indore with Hotels: 
    City:Mumbai with Hotels: 
    City:Sehore with Hotels: 
    

    You have 2 options. I think option #2 is by far superior, here.

  • Instead of ending up with a Map<String /* City */, List<Hotel>>, end up with a Map<String /* City */, List<String /* Name of hotel */>>. During your stream op, map the 'value' part using a downstream mapper: Collectors.mapping(Hotel::getHotelName, Collectors.toList()).

  • Keep the data structure exactly the same, but have your hotels print useful things when printed; they currently render as Hotel@7ba4f24f, because your Hotel class lacks a toString implementation and that's a bad state of affairs. To fix it, simply add one: Add a @Override public String toString() { ... } method to your Hotel.java file, which prints whatever you want to print. Perhaps as simple as: return this.hotelName;.

    NB: To make such things easy, you could look into using Project Lombok. Then all you'd need to write is:

    @lombok.Data public class Hotel {
        String name, city, phoneNumber;
    

    and the getters, setters, but also equals, hashCode, and toString are all just silently there, with sensible implementations.

    DISCLAIMER: I contribute to Project Lombok.

    I appreciate your suggestion but I have a different requirement where I don't want to edit my bean class in any manner. So I can not use any overridden method over there in Hotel. I already highlighted that in my question. – gaurav kulkarni Feb 21, 2021 at 15:41

    Thanks for contributing an answer to Stack Overflow!

    • Please be sure to answer the question. Provide details and share your research!

    But avoid

    • Asking for help, clarification, or responding to other answers.
    • Making statements based on opinion; back them up with references or personal experience.

    To learn more, see our tips on writing great answers.

  •  
    推荐文章