85

I'm trying to understand Java 8 streams. I have two classes:

public class UserMeal {
    protected final LocalDateTime dateTime;

    protected final String description;

    protected final int calories;

    public UserMeal(LocalDateTime dateTime, String description, int calories) {
        this.dateTime = dateTime;
        this.description = description;
        this.calories = calories;
    }

    public LocalDateTime getDateTime() {
        return dateTime;
    }

    public String getDescription() {
        return description;
    }

    public int getCalories() {
        return calories;
    }
}

and:

public class UserMealWithExceed {
    protected final LocalDateTime dateTime;

    protected final String description;

    protected final int calories;

    protected final boolean exceed;

    public UserMealWithExceed(LocalDateTime dateTime, String description, int calories, boolean exceed) {
        this.dateTime = dateTime;
        this.description = description;
        this.calories = calories;
        this.exceed = exceed;
    }
}

The exceed field should indicate whether the sum of calories for the entire day. This field is the same for all entries for that day.

I try to get object from List<UserMeal> mealList, group by the day, calculate calories for a period of time, and create List<UserMealWithExceed>:

public static List<UserMealWithExceed>  getFilteredMealsWithExceeded(List<UserMeal> mealList, LocalTime startTime, LocalTime endTime, int caloriesPerDay) {

    return mealList.stream()
            .filter(userMeal -> userMeal.getDateTime().toLocalTime().isAfter(startTime)&&userMeal.getDateTime().toLocalTime().isBefore(endTime))
            .collect(Collectors.groupingBy(userMeal -> userMeal.getDateTime().getDayOfMonth(),
                         Collectors.summingInt(userMeal -> userMeal.getCalories())))
            .forEach( ????? );
}

but I don't understand how to create new object in forEach and return collection.

How I see in pseudocode:

.foreach( 
    if (sumCalories>caloriesPerDay)
    {return new UserMealWithExceed(userMeal.getdateTime, usermeal.getDescription, usermeal.getCalories, true);}
    else
    {return new UserMealWithExceed(userMeal.getdateTime, usermeal.getDescription, usermeal.getCalories, false)
    }
)//foreach
1
  • 4
    Simply, don’t use forEach. A Stream supports more operations than this one. Commented Feb 29, 2016 at 13:26

3 Answers 3

180

If you want to iterate over a list and create a new list with "transformed" objects, you should use the map() function of stream + collect(). In the following example I find all people with the last name "l1" and each person I'm "mapping" to a new Employee instance.

public class Test {

    public static void main(String[] args) {
        List<Person> persons = Arrays.asList(
                new Person("e1", "l1"),
                new Person("e2", "l1"),
                new Person("e3", "l2"),
                new Person("e4", "l2")
        );

        List<Employee> employees = persons.stream()
                .filter(p -> p.getLastName().equals("l1"))
                .map(p -> new Employee(p.getName(), p.getLastName(), 1000))
                .collect(Collectors.toList());

        System.out.println(employees);
    }

}

class Person {

    private String name;
    private String lastName;

    public Person(String name, String lastName) {
        this.name = name;
        this.lastName = lastName;
    }

    // Getter & Setter
}

class Employee extends Person {

    private double salary;

    public Employee(String name, String lastName, double salary) {
        super(name, lastName);
        this.salary = salary;
    }

    // Getter & Setter
}
Sign up to request clarification or add additional context in comments.

7 Comments

Works perfect! I used similar pattern to convert a list of database entities to DTO's
What about the case were Employee only has a no-arg constructor?
@csmith49 you can instantiate the object and call the set methods inside the map function. You can have a better look on lambda expressions on tutorials.jenkov.com/java/lambda-expressions.html and baeldung.com/java-8-lambda-expressions-tips
Yeah thanks, I didn't see your message until now but worked it out myself yesterday, good to know this is the correct way to do it: List<Employee> employeeList = persons.stream() .filter(p -> p.getLastName().equals("l1")) .map(p -> { Employee e = new Employee(); e.setName(p.getName()); e.setSalary(p.getSalary()); return e; }) .collect(Collectors.toList());
Hi thanks for the answer, but how to return multiple objects, eg: .map(p -> { return p + new Employee(p.getName(), p.getLastName(), 1000) }). As I need to return new object with p.
|
20

What you are possibly looking for is map(). You can "convert" the objects in a stream to another by mapping this way:

...
 .map(userMeal -> new UserMealExceed(...))
...

Comments

6

An addition to the solution by @Rafael Teles. The syntactic sugar Collectors.mapping does the same in one step:

//...
List<Employee> employees = persons.stream()
  .filter(p -> p.getLastName().equals("l1"))
  .collect(
    Collectors.mapping(
      p -> new Employee(p.getName(), p.getLastName(), 1000),
      Collectors.toList()));

Detailed example can be found here

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.