У меня есть объектный процесс с датой и логическим индикатором ошибки. Я хочу получить общее количество процессов и количество процессов с ошибками для каждой даты. Так, например, 01 июня будет иметь счет 2, 1; 2 июня будет 1, 0, а июнь 03 1, 1. Единственный способ, которым я смог это сделать, - это потоковое воспроизведение дважды, чтобы получить счет. Я попытался реализовать собственный сборщик, но безуспешно. Есть ли элегантное решение вместо моего неуклюжего метода?
final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
final List<Process> processes = new ArrayList<>();
processes.add(new Process(sdf.parse("2016-06-01"), false));
processes.add(new Process(sdf.parse("2016-06-01"), true));
processes.add(new Process(sdf.parse("2016-06-02"), false));
processes.add(new Process(sdf.parse("2016-06-03"), true));
System.out.println(processes.stream()
.collect(
Collectors.groupingBy(Process::getDate, Collectors.counting()) ));
System.out.println(processes.stream().filter(order -> order.isHasError())
.collect(
Collectors.groupingBy(Process::getDate, Collectors.counting()) ));
private class Process {
private Date date;
private boolean hasError;
public Process(Date date, boolean hasError) {
this.date = date;
this.hasError = hasError;
}
public Date getDate() {
return date;
}
public boolean isHasError() {
return hasError;
}
}
Код после решения @glee8e и советов @Holger
Collector<Process, Result, Result> ProcessCollector = Collector.of(
() -> Result::new,
(r, p) -> {
r.increment(0);
if (p.isHasError()) {
r.increment(1);
}
}, (r1, r2) -> {
r1.add(0, r2.get(0));
r1.add(1, r2.get(1));
return r1;
});
Map<Date, Result> results = Processs.stream().collect(groupingBy(Process::getDate, ProcessCollector));
results.entrySet().stream().sorted(Comparator.comparing(Entry::getKey)).forEach(entry -> System.out
.println(String.format("date = %s, %s", sdf.format(entry.getKey()), entry.getValue())));
private class Result {
private AtomicIntegerArray array = new AtomicIntegerArray(2);
public int get(int index) {
return array.get(index);
}
public void increment(int index) {
array.getAndIncrement(index);
}
public void add(int index, int delta) {
array.addAndGet(index, delta);
}
@Override
public String toString() {
return String.format("totalProcesses = %d, totalErrors = %d", array.get(0), array.get(1));
}
}
1 ответ
Желательно, чтобы мы добавляли POJO для хранения результата, иначе функция объединения может выглядеть немного неясной. Я объявил POJO общедоступным, но вы можете изменить его, если считаете, что лучше его скрыть.
public class Result {
public int all, error;
}
Основной код:
// Add it somewhere in this file.
private static final Set <Characteristics> CH_ID = Collections.unmodifiableSet(EnumSet.of(Collector.Characteristics.IDENTITY_FINISH));
//...
// This is main processing code
processes.stream().collect(collectingAndThen(groupingBy(Process::getDate, new Collector<Process, Result, Result> {
@Override
public Supplier<Result> supplier() {
return Result::new;
}
@Override
public BiConsumer<Process, Result> accumlator() {
return (p, r) -> {
r.total++;
if (p.isHasError())
r.error++;
};
}
@Override
public BinaryOperator<Result> combiner() {
return (r1, r2) -> {
r1.total += r2.total;
r1.error += r2.error;
return r1;
};
}
@Override
public Function<Result, Result> finisher() {
return Function.identity();
}
@Override
public Set<Characteristics> characteristics() {
return CH_ID;
}
})));
PS: Я полагаю, у вас есть import static java.util.stream.Collectors
Collector.of(…)
, который позволяет указать три функции (без финишера идентичности) без необходимости во внутреннем классе. Вам даже не нужно беспокоиться об указании характеристики IDENTITY_FINISH
, этот метод уже выведет это из-за отсутствия такой функции. Но вы можете рассмотреть возможность указания характеристики UNORDERED
…
interface Collector
, а не в служебной программе class Collectors
, что легко упустить из виду, поскольку мы привыкли искать только в указанном служебном классе. Я помню похожие неудачные поиски определенного удобного метода в Integer
, чтобы позже узнать, что он находится в Math
(или наоборот)…
Похожие вопросы
Новые вопросы
java
Java — это высокоуровневый объектно-ориентированный язык программирования. Используйте этот тег, если у вас возникли проблемы с использованием или пониманием самого языка. Этот тег часто используется вместе с другими тегами для библиотек и/или фреймворков, используемых разработчиками Java.
List
доCollection
во второй строке.