Java 8 – Update two properties in same Stream code

Solution for Java 8 – Update two properties in same Stream code
is Given Below:

I’m wondering if there is a way I can update two times an object in a Stream lambda code, I need to update two properties of a class, I need to update the value and the recordsCount properties

Object:

public class HistoricDataModelParsed {

private Date   startDate;
private Date   endDate;
private Double value;
private int    recordsCount;

}

I tried doing something like this:

val existingRecord = response.stream()
     .filter(dateTime ->fromDate.equals(dateTime.getStartDate()))
     .findAny()
     .orElse(null);


response.stream()
     .filter(dateTime ->fromDate.equals(dateTime.getStartDate()))
     .findAny()
     .orElse(existingRecord)
     .setValue(valueAdded)
     .setRecordsCount(amount);

But I got this error: “Cannot invoke setRecordsCount(int) on the primitive type void”

So I ended up doing the stream two times to update each of the two fields I needed

response.stream()
     .filter(dateTime ->fromDate.equals(dateTime.getStartDate()))
     .findAny()
     .orElse(existingRecord)
     .setValue(valueAdded);
                
 response.stream()
     .filter(dateTime ->fromDate.equals(dateTime.getStartDate()))
     .findAny()
     .orElse(existingRecord)
     .setRecordsCount(amount);      

Is there a way I can achieve what I need without the need to stream two times the list?

The return type of setValue is void and not HistoricDataModelParsed. So you cannot invoke the method setRecordsCount which is in HistoricDataModelParsed class.

You could have added a method in HistoricDataModelParsed which takes two parameters for value and recordsCount:

public void setValueAndCount(Double value, int count) {
    this.value = value;
    this.recordsCount = count;
}

Then call this method after orElse:

response.stream()
     .filter(dateTime ->fromDate.equals(dateTime.getStartDate()))
     .findAny()
     .orElse(existingRecord)
     .setValueAndCount(valueAdded, amount);

The state of an object should not change within a stream. It can lead to inconsistent results. But you can create new instances of the objects and pass new values via the constructor. Here is a simple record that demonstrates the method. Records are basically immutable classes that have no setters. The getters are the names of the variables. A class would also work in this example.

record Temp(int getA, int getB) {
    @Override
    public String toString() {
        return "[" + getA + ", " + getB  +"]";
    }
}

Some data

    List<Temp> list = List.of(new Temp(10, 20), new Temp(50, 200),
        new Temp(100, 200));

And the transformation. A new instance of Temp with new values is created along with the old ones to completely populate the constructor. Otherwise, the existing object is passed along.

List<Temp> result = list.stream().map(
        t -> t.getA() == 50 ? new Temp(2000, t.getB()) : t)
        .toList();

System.out.println(result);

Prints

[[10, 20], [2000, 200], [100, 200]]

To answer the void error you got it’s because a stream expects values to continue thru out the stream so if a method is void, it isn’t returning anything so you would have to return it. Here is an example:

stream.map(t->{voidReturnMethod(t); return t;}).toList();

The return ensures the pipeline continues.

Simply store the result of orElse and then call your methods on it.

HistoricDataModelParsed record = 
    response.stream()
        .filter(dateTime -> fromDate.equals(dateTime.getStartDate()))
        .findAny()
        .orElse(existingRecord);

record.setValue(valueAdded)
record.setRecordsCount(amount);