Reputation: 73
I defined a map and filled it with 5 objects of the type Rectangle
. Each Rectangle-object has the attributes rectangleId
, aLength
, bLength
and color
.
I want to use the java stream api to stream the given map into the new map. While streaming, the value of bLength
shall be increased by 100 for all rectangles that are red
. Below is what I got so far. I can't seem to figure out how to change the value of bLength
.
public class Main {
public static void main(String[] args){
Rectangle r1 = new Rectangle(800, 100,200,"green");
Rectangle r2 = new Rectangle(900, 200,300,"red");
Rectangle r3 = new Rectangle(1000, 300,400,"yellow");
Rectangle r4 = new Rectangle(1100, 400,500,"blue");
Rectangle r5 = new Rectangle(1200, 500,600,"orange");
TreeMap<Integer, Rectangle> myMap = new TreeMap<>();
myMap.put(r1.rectangleId, r1);
myMap.put(r2.rectangleId, r2);
myMap.put(r3.rectangleId, r3);
myMap.put(r4.rectangleId, r4);
myMap.put(r5.rectangleId, r5);
Map<Integer, Rectangle> myMapNew = myMap.entrySet().stream()
.filter(r -> r.getValue().color == "red")
.collect(Collectors.toMap(r -> r.getKey(), r -> r.getValue()));
}
public class Rectangle {
public int rectangleId;
public int aLength;
public int bLength;
public String color;
public Rectangle(int rectangleId, int aLength, int bLength, String color){
this.rectangleId = rectangleId;
this.aLength = aLength;
this.bLength = bLength;
this.color = color;
}
}
Upvotes: 1
Views: 4136
Reputation: 9418
So you have 2 steps in your streaming then:
Rectangle
to outgoing Rectangle
(= identity)Rectangle
based on a predicate (= conditional attribute-modification)Function<Rectangle, Rectangle> toIdentical = Function.identity();
bLength
shall be increased by100
for allRectangle
s that have propertycolor
with value"red"
Function<Rectangle, Rectangle> toConditialModified = (rectangle) -> {
if (rectangle.color.equals("red")) { // conditional
rectangle.bLength += 100; // modified
}
return rectangle;
};
Build a pipeline using these functional steps:
Map<Integer, Rectangle> newRectanglesMap = oldRectanglesMap.values().stream()
.map( toIdentical ) // can leave this out
.map( toConditionalModified )
.collect(Collectors.toMap(r -> r.rectangleId, r -> r));
Just work on the values, modify if needed in-place:
oldRectanglesMap.values().stream()
.filter(r -> r.color.equals("red")) // get only the red ones
.forEach(r -> { r.bLength += 100; }) // modify the filtered
This would be better readable if decomposing Function<Rectangle, Rectangle> toConditialModified
of previous step 2:
Predicate<Rectangle> isRed = r -> r.color.equals("red");
Consumer<Rectangle> updateBLengthPlus100 = r -> r.bLength += 100;
oldRectanglesMap.values().stream()
.filter( isRed )
.forEach( updateBLengthPlus100 );
I just played with your code and requirement:
Upvotes: 2
Reputation: 425448
By modifying the rectangles, there is still only one instance of each, so the rectangles in both maps are the same rectangles and so new map would be identical to the old map after your proposed operation. Thus a new map is not required - modifying the old one is sufficient:
myMap.values().stream()
.filter(r -> r.color.equals("red"))
.forEach(r -> r.length += 100);
For the new map to be different, you would need to create new instance of the rectangles for the néw map, but that is not what you proposed.
Also note that comparing Strings using ==
is not guaranteed to work the way you expect in the general case; always use .equals()
.
Upvotes: 4
Reputation: 2575
If you want to have all of the rectangles from the original map in the new map (and modify some of them) you can't use the filter
method, because that will restrict the result to only the red ones.
You need to use the map
method to change the rectangles while streaming:
Map<Integer, Rectangle> myMapNew = myMap.entrySet().stream()
.map(entry -> new AbstractMap.SimpleEntry<>(entry.getKey(), entry.getValue().increaseBLengthOrReturnIdentity()))
.collect(Collectors.toMap(r -> r.getKey(), r -> r.getValue()));
And you need to define the method increaseBLengthOrReturnIdentity
in you Rectangle
class:
public class Rectangle {
public int rectangleId;
public int aLength;
public int bLength;
public String color;
public Rectangle(int rectangleId, int aLength, int bLength, String color){
this.rectangleId = rectangleId;
this.aLength = aLength;
this.bLength = bLength;
this.color = color;
}
public Rectangle increaseBLengthOrReturnIdentity() {
if (color.equals("red")) {//btw.: better use equals to compare strings instead of ==
return new Rectangle(rectangleId, aLength, bLength + 100, color);
}
// you could also return 'this' here; depends on whether you need a new object or not
return new Rectangle(rectangleId, aLength, bLength, color);
}
}
Upvotes: 1