I'm currently playing around with Java 8 features .
I have the following piece of code, and tried out multiple ways to use Streams, but without success.
for (CheckBox checkBox : checkBoxList) {
for (String buttonFunction : buttonFunctionsList) {
if (checkBox.getId().equals(buttonFunction)) {
associatedCheckBoxList.add(checkBox);
}
}
}
I tried the following, but I am not sure is this correct or not:
checkBoxList.forEach(checkBox -> {
buttonFunctionsList.forEach(buttonFunction -> {
if (checkBox.getId().equals(buttonFunction))
associatedCheckBoxList.add(checkBox);
});
});
Thanks!
Eran's answer is probably fine; but since buttonFunctionList is (presumably) a List, there is a possibility of it containing duplicate elements, meaning that the original code would add the checkboxes to the associated list multiple times.
So here is an alternative approach: you are adding the checkbox to the list as many times as there are occurrences of that item's id in the other list.
As such, you can write the inner loop as:
int n = Collections.frequency(buttonFunctionList, checkBox.getId();
associatedCheckboxList.addAll(Collections.nCopies(checkBox, n);
Thus, you can write this as:
List<CheckBox> associatedCheckBoxList =
checkBoxList.flatMap(cb -> nCopies(cb, frequency(buttonFunctionList, cb.getId())).stream())
.collect(toList());
(Using static imports for brevity)
If either checkBoxList or buttonFunctionList is large, you might want to consider computing the frequencies once:
Map<String, Long> frequencies = buttonFunctionList.stream().collect(groupingBy(k -> k, counting());
Then you can just use this in the lambda as the n parameter of nCopies:
(int) frequencies.getOrDefault(cb.getId(), 0L)
You should prefer collect over forEach when your goal is to produce some output Collection:
List<CheckBox> associatedCheckBoxList =
checkBoxList.stream()
.filter(cb -> buttonFunctionsList.stream().anyMatch(bf -> cb.getId().equals(bf)))
.collect(Collectors.toList());
Related
Consider the following code:
List<Integer> odd = new ArrayList<Integer>();
List<Integer> even = null;
List<Integer> myList = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
even = myList.stream()
.filter(item -> {
if(item%2 == 0) { return true;}
else {
odd.add(item);
return false;
}
})
.collect(Collectors.toList());
What I am trying to do here is get the even and odd values from a list into separate lists.
The stream filter() method returns true for even items and the stream collector will collect them.
For the odd case, the filter will return false and the item will never reach the collector.
So I am adding such odd numbers in another list I created before under the else block.
I know this is not an elegant way of working with streams. For example if I use a parallel stream then there will be thread safety issue with the odd list. I cannot run it multiple times with different filters because of performance reasons (should be O(n)).
This is just an example for one use-case, the list could contain any object and the lambda inside the filter needs to separate them based on some logic into separate lists.
In simple terms: from a list create multiple lists containing items separated based on some criteria.
Without streams it would be just to run a for loop and do simple if-else and collect the items based on the conditions.
Here is an example of how you could separate elements (numbers) of this list in even and odd numbers:
List<Integer> myList = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
Map<Boolean, List<Integer>> evenAndOdds = myList.stream()
.collect(partitioningBy(i -> i % 2 == 0));
You would get lists of even/odd numbers like this (either list may be empty):
List<Integer> even = evenAndOdds.get(true);
List<Integer> odd = evenAndOdds.get(false);
You could pass any lambda with required logic in partitioningBy.
I am trying to find the best way to optimise the converters below to follow the flow I call 'convertAndGroupForUpdate' first which triggers the conversions and relevant mappings.
Any help to optimise this code would be massively appreciated.
public List<GroupedOrderActionUpdateEntity> convertAndGroupForUpdate(List<SimpleRatifiableAction> actions) {
List<GroupedOrderActionUpdateEntity> groupedActions = new ArrayList<>();
Map<String, List<SimpleRatifiableAction>> groupSimple = actions.stream()
.collect(Collectors.groupingBy(x -> x.getOrderNumber() + x.getActionType()));
groupSimple.entrySet().stream()
.map(x -> convertToUpdateGroup(x.getValue()))
.forEachOrdered(groupedActions::add);
return groupedActions;
}
public GroupedOrderActionUpdateEntity convertToUpdateGroup(List<SimpleRatifiableAction> actions) {
List<OrderActionUpdateEntity> actionList = actions.stream().map(x -> convertToUpdateEntity(x)).collect(Collectors.toList());
return new GroupedOrderActionUpdateEntity(
actions.get(0).getOrderNumber(),
OrderActionType.valueOf(actions.get(0).getActionType()),
actions.get(0).getSource(),
12345,
actions.stream().map(SimpleRatifiableAction::getNote)
.collect(Collectors.joining(", ", "Group Order Note: ", ".")),
actionList);
}
public OrderActionUpdateEntity convertToUpdateEntity(SimpleRatifiableAction action) {
return new OrderActionUpdateEntity(action.getId(), OrderActionState.valueOf(action.getState()));
}
You can’t elide a grouping operation, but you don’t need to store the intermediate result in a local variable.
Further, you should not add to a list manually, when you can collect to a List. Just do it like you did in the other method.
Also, creating a grouping key via string concatenation is tempting, but very dangerous, depending on the contents of the properties, the resulting strings may clash. And string concatenation is rather expensive. Just create a list of the property values, as long as you don’t modify it, it provides the right equality semantics and hash code implementation.
If you want to process the values of a map only, don’t call entrySet(), to map each entry via getValue(). Just use values() in the first place.
public List<GroupedOrderActionUpdateEntity> convertAndGroupForUpdate(
List<SimpleRatifiableAction> actions) {
return actions.stream()
.collect(Collectors.groupingBy( // use List.of(…, …) in Java 9 or newer
x -> Arrays.asList(x.getOrderNumber(), x.getActionType())))
.values().stream()
.map(x -> convertToUpdateGroup(x))
.collect(Collectors.toList());
}
Since convertToUpdateGroup is processing the list of actions of each group multiple times, there is not much that can be simplified and I wouldn’t inline it either. If there was only one operation, e.g. joining them to a string, you could do that right in the groupingBy operation, but there is no simply way to collect to multiple results.
Consider the following code:
List<Integer> odd = new ArrayList<Integer>();
List<Integer> even = null;
List<Integer> myList = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
even = myList.stream()
.filter(item -> {
if(item%2 == 0) { return true;}
else {
odd.add(item);
return false;
}
})
.collect(Collectors.toList());
What I am trying to do here is get the even and odd values from a list into separate lists.
The stream filter() method returns true for even items and the stream collector will collect them.
For the odd case, the filter will return false and the item will never reach the collector.
So I am adding such odd numbers in another list I created before under the else block.
I know this is not an elegant way of working with streams. For example if I use a parallel stream then there will be thread safety issue with the odd list. I cannot run it multiple times with different filters because of performance reasons (should be O(n)).
This is just an example for one use-case, the list could contain any object and the lambda inside the filter needs to separate them based on some logic into separate lists.
In simple terms: from a list create multiple lists containing items separated based on some criteria.
Without streams it would be just to run a for loop and do simple if-else and collect the items based on the conditions.
Here is an example of how you could separate elements (numbers) of this list in even and odd numbers:
List<Integer> myList = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
Map<Boolean, List<Integer>> evenAndOdds = myList.stream()
.collect(partitioningBy(i -> i % 2 == 0));
You would get lists of even/odd numbers like this (either list may be empty):
List<Integer> even = evenAndOdds.get(true);
List<Integer> odd = evenAndOdds.get(false);
You could pass any lambda with required logic in partitioningBy.
Suppose i have multiple java 8 streams that each stream potentially can be converted into Set<AppStory> , now I want with the best performance to aggregate all streams into one DISTINCT stream by ID , sorted by property ("lastUpdate")
There are several ways to do what but i want the fastest one , for example:
Set<AppStory> appStr1 =StreamSupport.stream(splititerato1, true).
map(storyId1 -> vertexToStory1(storyId1).collect(toSet());
Set<AppStory> appStr2 =StreamSupport.stream(splititerato2, true).
map(storyId2 -> vertexToStory2(storyId1).collect(toSet());
Set<AppStory> appStr3 =StreamSupport.stream(splititerato3, true).
map(storyId3 -> vertexToStory3(storyId3).collect(toSet());
Set<AppStory> set = new HashSet<>();
set.addAll(appStr1)
set.addAll(appStr2)
set.addAll(appStr3) , and than make sort by "lastUpdate"..
//POJO Object:
public class AppStory implements Comparable<AppStory> {
private String storyId;
private String ........... many other attributes......
public String getStoryId() {
return storyId;
}
#Override
public int compareTo(AppStory o) {
return this.getStoryId().compareTo(o.getStoryId());
}
}
... but it is the old way.
How can I create ONE DISTINCT by ID sorted stream with BEST PERFORMANCE
somethink like :
Set<AppStory> finalSet = distinctStream.sort((v1, v2) -> Integer.compare('not my issue').collect(toSet())
Any Ideas ?
BR
Vitaly
I think the parallel overhead is much greater than the actual work as you stated in the comments. So let your Streams do the job in sequential manner.
FYI: You should prefer using Stream::concat because slicing operations like Stream::limit can be bypassed by Stream::flatMap.
Stream::sorted is collecting every element in the Stream into a List, sort the List and then pushing the elements in the desired order down the pipeline. Then the elements are collected again. So this can be avoided by collecting the elements into a List and do the sorting afterwards. Using a List is a far better choice than using a Set because the order matters (I know there is a LinkedHashSet but you can't sort it).
This is the in my opinion the cleanest and maybe the fastest solution since we cannot prove it.
Stream<AppStory> appStr1 =StreamSupport.stream(splititerato1, false)
.map(this::vertexToStory1);
Stream<AppStory> appStr2 =StreamSupport.stream(splititerato2, false)
.map(this::vertexToStory2);
Stream<AppStory> appStr3 =StreamSupport.stream(splititerato3, false)
.map(this::vertexToStory3);
List<AppStory> stories = Stream.concat(Stream.concat(appStr1, appStr2), appStr3)
.distinct().collect(Collectors.toList());
// assuming AppStory::getLastUpdateTime is of type `long`
stories.sort(Comparator.comparingLong(AppStory::getLastUpdateTime));
I can't guarantee that this would be faster than what you have (I guess so, but you'll have to measure to be sure), but you can simply do this, assuming you have 3 streams:
List<AppStory> distinctSortedAppStories =
Stream.of(stream1, stream2, stream3)
.flatMap(Function.identity())
.map(this::vertexToStory)
.distinct()
.sorted(Comparator.comparing(AppStory::getLastUpdate))
.collect(Collectors.toList());
I´m using Java 8 Stream where I iterate over two collections, and after pass a filter I want to sum one of the bigdecimal variables that I have inside my stream to an external bigDecimal variable "restrictionsNumber"
Here my code:
final BigDecimal restrictionsNumber = cmd.amount.getNumberOfUnits();
order.products()
.stream()
.flatMap(product -> product.getRestrictions()
.stream()
.filter(restriction -> restriction.equals(newProductRestriction))
.map(restriction -> restrictionsNumber.add(product.getAmount()
.getNumberOfUnits())));
The last map is the one where I´m trying to sum the two bigdecimals.
I know I´m doing something wrong.
Can anyone give me an advise about how to do it with Stream.
I´m trying to refactor from this old fashion code
final BigDecimal restrictionsNumber = cmd.amount.getNumberOfUnits();
for (Product product : order.products()) {
for (String oldProductRestriction : product.getRestrictions()) {
if (oldProductRestriction.equals(newProductRestriction)) {
restrictionsNumber = restrictionsNumber.add(product.getAmount()
.getNumberOfUnits());
}
}
}
Regards.
This may be what you need (but it keeps adding the same amount several times for each product, in line with your original code, which seems weird):
BigDecimal sum = order.products()
.stream()
.flatMap(product -> product.getRestrictions()
.stream()
.filter(restriction -> restriction.equals(newProductRestriction))
.map(restriction -> product.getAmount().getNumberOfUnits()))
.reduce(BigDecimal.ZERO, BigDecimal::add);
BigDecimal result = restrictionsNumber.add(sum);
It sounds like you want to use the "reduce" operation.
Reduce is used for operations like summing over a whole stream, or adding finding the maximum.
(If you want your addition to happen for a single stream element then your question was unclear to me, please add detail)