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.
–
–
–
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));
–
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.
–
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.