user663724
user663724

Reputation:

Using Group By in Java 8 with Property

package com;

import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

public class GroupByDemoInJava8 
{
    public static void main(String args[]) throws IOException
    {
        List<Person> people = new ArrayList<>();  // Date Format is MM/DD/YYYY
        people.add(new Person("John", "London", 21 , "2/1/18"));
        people.add(new Person("Swann", "London", 21 , "3/5/18" ));
        people.add(new Person("Kevin", "London", 23 , "3/12/18"));
        people.add(new Person("Monobo", "Tokyo", 23 , "4/18/18"));
        people.add(new Person("Sam", "Paris", 23 , "7/12/18"));
        people.add(new Person("Nadal", "Paris", 31, "4/2/19")); 
        Map<String,List<Person>> personByCity = new HashMap<>(); 

        SimpleDateFormat sdf = new SimpleDateFormat("MM/dd/yy");


        personByCity = people.stream() .collect(Collectors.groupingBy(sdf.parse(Person::getDateOfBirth))); 


         for (Map.Entry<String,List<Person>> entry : personByCity.entrySet()) { 
                System.out.println("Key = " + entry.getKey());
                System.out.println("Value = " + entry.getValue());
        } 

    }
}


class Person
{ 
    private String name; 
    private String city; private int age; private String dateOfBirth;

    public String getDateOfBirth() {
        return dateOfBirth;
    }

    public void setDateOfBirth(String dateOfBirth) {
        this.dateOfBirth = dateOfBirth;
    }

    public Person(String name, String city, int age , String dateOfBirth) 
    { 
        this.name = name; this.city = city; this.age = age;this.dateOfBirth=dateOfBirth;
    }

    public String getName()
    { return name; } 
    public void setName(String name) 
    { this.name = name; } 
    public String getCity() 
    { return city; } 
    public void setCity(String city)
    { this.city = city; } public int getAge() { return age; } public void setAge(int age) { this.age = age; }

    @Override
    public String toString() {
        return "Person [name=" + name + ", city=" + city + ", age=" + age + "]";
    }
}

Unfortunately, I get the following compilation error

  • The method parse(String) in the type DateFormat is not applicable for the arguments (Person::getDateOfBirth)
  • The target type of this expression must be a functional interface

Upvotes: 2

Views: 285

Answers (2)

Ryuzaki L
Ryuzaki L

Reputation: 40008

Method reference operator is used to replace the lambda expression if it is a direct method call, But in your case you are trying to do groupingBy based on the result of sdf.parse

Method References

You use lambda expressions to create anonymous methods. Sometimes, however, a lambda expression does nothing but call an existing method. In those cases, it's often clearer to refer to the existing method by name. Method references enable you to do this; they are compact, easy-to-read lambda expressions for methods that already have a name.

And also parse method from SimpleDateFormat throws ParseException were you need to handle it, in addition to that it returns java.util.Date so you need to change return type to Map<Date,List<Person>>

public Date parse(String source) throws ParseException

Still if you wanna use SimpleDateFormat you need to handle the exception

 Map<Date,List<Person>> personByCity = people.stream() 
                      .collect(Collectors.groupingBy(person->{
                          try {
                             return sdf.parse(person.getDateOfBirth());
                           }
                           catch(ParseException ex){
                                //log error
                              }
                            return new Date();
                         });

The recommended approach i prefer is use LocalDate by using DateTimeFormatter

DateTimeFormatter formatter = DateTimeFormatter.ofPattern("MM/dd/yy");

 Map<LocalDate,List<Person>> personByCity = people.stream() 
                      .collect(Collectors.groupingBy(person->LocalDate.parse(person.getDateOfBirth(),formatter)); 

Note Your input date string "2/1/18" and format pattern "MM/dd/yy" both are valid but the return type will be changed based on the implementation, below is the example

DateTimeFormatter formatter = DateTimeFormatter.ofPattern("MM/dd/yy");

LocalDate date = LocalDate.parse("02/01/18", formatter);

System.out.println(date);      //2018-02-01

SimpleDateFormat sdf = new SimpleDateFormat("MM/dd/yy");

Date d = sdf.parse("02/01/18");

System.out.println(d);       //Thu Feb 01 00:00:00 CST 2018

Upvotes: 4

Nicholas K
Nicholas K

Reputation: 15423

Firstly you say the dates are in the format MM/DD/YYYY, but in fact they are not. Your data should be defined:

List<Person> people = new ArrayList<>(); // Date Format is MM/DD/YYYY
people.add(new Person("John", "London", 21, "02/01/2018"));
people.add(new Person("Swann", "London", 21, "03/05/2018"));
people.add(new Person("Kevin", "London", 23, "03/12/2018"));
people.add(new Person("Monobo", "Tokyo", 23, "04/18/2018"));
people.add(new Person("Sam", "Paris", 23, "07/12/2018"));
people.add(new Person("Nadal", "Paris", 31, "04/02/2019"));

Secondly do not use SimpleDateFormat it is long outdated, use DateTimeFormatter and LocalDatefrom java 1.8 instead:

Map<LocalDate, List<Person>> personByCity = new HashMap<>();
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("MM/dd/yyyy");
personByCity = people.stream()
            .collect(Collectors.groupingBy(p -> LocalDate.parse(p.getDateOfBirth(), dtf)));

Upvotes: 1

Related Questions