Khi bạn lọc chúng khỏi luồng, không có cách nào để biết liệu tất cả số dư có là null
hay không.
Làm hai đi ngang qua các dữ liệu có lẽ là giải pháp thẳng về phía trước, và tôi có lẽ sẽ đi với điều đó đầu tiên:
boolean allNulls = account.stream().map(Account::getBalance).allMatch(Objects::isNull);
Long sum = allNulls ? null : account.stream().map(Account::getBalance).filter(Objects::nonNull).mapToLong(l -> l).sum();
Bạn có thể thoát khỏi bước lọc với giải pháp của bạn với reduce
, mặc dù khả năng đọc có thể không phải là tốt nhất:
Long sum = account.stream()
.reduce(null, (l1, l2) -> l1 == null ? l2 :
l2 == null ? l1 : Long.valueOf(l1 + l2));
Thông báo cuộc gọi Long.valueOf
. Đó là để tránh rằng các loại biểu thức có điều kiện là long
, và do đó một NPE trên một số trường hợp cạnh.
Một giải pháp khác là sử dụng API
Optional
.Đầu tiên, tạo một
Stream<Optional<Long>>
từ các giá trị của các số dư và giảm họ:
Optional<Long> opt = account.stream()
.map(Account::getBalance)
.flatMap(l -> Stream.of(Optional.ofNullable(l)))
.reduce(Optional.empty(),
(o1, o2) -> o1.isPresent() ? o1.map(l -> l + o2.orElse(0L)) : o2);
này sẽ cung cấp cho bạn một Optional<Long>
rằng sẽ trống nếu tất cả các giá trị là null
, nếu không nó sẽ cung cấp cho bạn tổng các phi giá trị null.
Hoặc bạn có thể muốn tạo ra một nhà sưu tập tùy chỉnh cho việc này:
class SumIntoOptional {
private boolean allNull = true;
private long sum = 0L;
public SumIntoOptional() {}
public void add(Long value) {
if(value != null) {
allNull = false;
sum += value;
}
}
public void merge(SumIntoOptional other) {
if(!other.allNull) {
allNull = false;
sum += other.sum;
}
}
public OptionalLong getSum() {
return allNull ? OptionalLong.empty() : OptionalLong.of(sum);
}
}
và sau đó:
OptionalLong opt = account.stream().map(Account::getBalance).collect(SumIntoOptional::new, SumIntoOptional::add, SumIntoOptional::merge).getSum();
Như bạn có thể thấy, có rất nhiều cách để đạt được điều này, vì vậy tôi lời khuyên sẽ là: chọn đầu tiên dễ đọc nhất. Nếu các vấn đề về hiệu suất phát sinh với giải pháp của bạn, hãy kiểm tra xem nó có thể được cải thiện hay không (bằng cách chuyển song song luồng hoặc sử dụng một giải pháp thay thế khác). Nhưng đo lường, đừng đoán.
Tốt nhất là chỉ cần chia nhỏ thành 2 dòng, một để lọc ra 'null' và một để trả về' null' nếu kích thước là '0', tổng khác. Có thể thực hiện nó trong một dòng bằng cách sử dụng 'reduce' thay vì' filter' nhưng khả năng đọc sẽ bị ảnh hưởng. –
Thực ra đó là thực tế tồi để trả lại giá trị rỗng (xem mẫu NullObject) ... – Maksym
@pbabcdefp - ah, cảm ơn. Đúng, tôi đã làm lại nó thành hai dòng như bạn đề nghị, và nó chắc chắn là dễ đọc hơn. – user384842