This is a simple task from some website.
If a[i] > b[i], then Alice is awarded 1 point. If a[i] < b[i], then Bob is awarded 1 point. If a[i] = b[i], then neither person receives a point. Return: Alice's score is in the first position, and Bob's score is in the second.
Can I write this code using stream?
public static List<Integer> compareTriplets(List<Integer> a, List<Integer> b) {
int alice = 0;
int bob = 0;
for (int i = 0; i < a.size(); i++) {
if (a.get(i) > b.get(i))
alice++;
else if (a.get(i) < b.get(i))
bob++;
}
return Arrays.asList(alice, bob);
}
...
After a little thought, I wrote this code:
public static List<Integer> compareTriplets(List<Integer> a, List<Integer> b) {
Integer[] result = {0, 0};
IntStream.range(0, Math.min(a.size(), b.size()))
.map(i -> a.get(i).compareTo(b.get(i)))
.forEach(i -> {
if (i > 0) result[0]++;
else if (i < 0) result[1]++;
});
return Arrays.asList(result);
}
But this code is not much more concise. Are there ways to make the code more beautiful?
You can make the code shorter with filter and count, but using loops is cleaner here. Streams are not always the solution.
return Arrays.asList(
IntStream.range(0, Math.min(a.size(), b.size())).filter(i -> a.get(i) > b.get(i)).count(),
IntStream.range(0, Math.min(a.size(), b.size())).filter(i -> a.get(i) < b.get(i)).count()
);
partitioningBy() & counting()One of the way to solve this problem with streams performing only one iteration over the given set of data is to make use of the built Collectors partitioningBy() and counting():
public static List<Integer> compareTriplets(List<Integer> a, List<Integer> b) {
return IntStream.range(0, a.size())
.map(i -> a.get(i) - b.get(i))
.filter(i -> i != 0)
.boxed()
.collect(Collectors.collectingAndThen(
Collectors.partitioningBy(i -> i > 0, Collectors.counting()),
map -> Arrays.asList(map.get(true).intValue(), map.get(false).intValue())
));
}
CollectorAnother option would be to define a custom Collector using static factory method Collector.of(). As well as previous approach, it would allow to process the data using a single stream:
public static List<Integer> compareTriplets(List<Integer> a, List<Integer> b) {
return IntStream.range(0, a.size())
.boxed()
.collect(Collector.of(
() -> new int[]{0, 0},
(int[] score, Integer i) -> {
if (a.get(i) > b.get(i)) score[0]++;
if (b.get(i) > a.get(i)) score[1]++;
},
(int[] left, int[] right) -> {
Arrays.setAll(left, i -> left[i] + right[i]);
return left;
},
arr -> Arrays.asList(arr[0], arr[1])
));
}
teeing()Another option that would allow to produce the result using a single stream is Java 12 Collector teeing(), which expects two downstream Collectors and a Function which performs a final transformation by merging the results they produced.
public static List<Integer> compareTriplets(List<Integer> a, List<Integer> b) {
return IntStream.range(0, a.size())
.boxed()
.collect(Collectors.teeing(
Collectors.filtering(i -> a.get(i) > b.get(i), Collectors.counting()),
Collectors.filtering(i -> b.get(i) > a.get(i), Collectors.counting()),
(alice, bob) -> List.of(alice.intValue(), bob.intValue())
));
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With