Java lambda para devolver nulo si la lista está vacía, de lo contrario, ¿suma de valores?

Si quiero totalizar una lista de los saldos actuales de las cuentas, puedo hacer:

accountOverview.setCurrentBalance(account.stream().
                filter(a -> a.getCurrentBalance() != null).
                mapToLong(a -> a.getCurrentBalance()).
                sum());

Pero esta expresión devolverá 0, incluso si todos los saldos son nulos. Me gustaría que devuelva nulo si todos los saldos son nulos, 0 si hay saldos 0 no nulos y la suma de los saldos de lo contrario.

¿Cómo puedo hacer esto con una expresión lambda?

Muchas gracias

Respuesta 1

Una vez que los filtró de la transmisión, no hay forma de saber si todos los saldos fueron null(a menos que verifique lo que count()regresa, pero no podrá usar la transmisión ya que es una operación terminal).

Hacer dos pasadas sobre los datos es probablemente la solución directa, y probablemente iría con eso primero:

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();

Puede deshacerse del paso de filtrado con su solución reduce, aunque la legibilidad tal vez no sea la mejor:

Long sum = account.stream()
                  .reduce(null, (l1, l2) -> l1 == null ? l2 :
                                                         l2 == null ? l1 : Long.valueOf(l1 + l2));

Note la Long.valueOfllamada. Es para evitar que el tipo de expresión condicional sealong , y por lo tanto, un NPE en algunos casos extremos.


Otra solución sería usar la OptionalAPI. Primero, cree un a Stream<Optional<Long>>partir de los valores de los saldos y reduzca:

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);

Esto le dará un valor Optional<Long>que estará vacío si todos los valores lo fueran null; de lo contrario, le dará la suma de los valores no nulos.

O es posible que desee crear un recopilador personalizado para esto:

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);
    }
}

y entonces:

OptionalLong opt = account.stream().map(Account::getBalance).collect(SumIntoOptional::new, SumIntoOptional::add, SumIntoOptional::merge).getSum();


Como puede ver, hay varias formas de lograr esto, por lo que mi consejo sería: elija el más legible primero. Si surgen problemas de rendimiento con su solución, verifique si podría mejorarse (girando la transmisión en paralelo o usando otra alternativa). Pero mida, no adivine.

Respuesta: 2

Por ahora, voy con esto. Pensamientos?

        accountOverview.setCurrentBalance(account.stream().
                filter(a -> a.getCurrentBalance() != null).
                map(a -> a.getCurrentBalance()).
                reduce(null, (i,j) -> { if (i == null) { return j; } else { return i+j; } }));

Debido a que ya he filtrado nulos, estoy garantizado de no acertar ninguno. Al hacer el parámetro inicial para reducir 'nulo', puedo asegurarme de volver a ser nulo en una lista vacía.

Sin embargo, se siente un poco difícil / confuso de leer. Quisiera una mejor solución.

EDITAR Gracias a pbabcdefp, he optado por esta solución bastante más respetable:

        List<Account> filtered = account.stream().
                filter(a -> a.getCurrentBalance() != null).
                collect(Collectors.toList());

        accountOverview.setCurrentBalance(filtered.size() == 0?null:
            filtered.stream().mapToLong(a -> a.getCurrentBalance()).
            sum());
Respuesta: 3

No estoy seguro de si este es el lugar, pero estoy fuera de ideas, el siguiente es un código de actualización para mi programa, es simple, pero por alguna razón no funciona. privado vacío txt_updateActionPerformed (java.awt ....

Necesitamos fusionar dos conjuntos de datos que tienen nombres de columna diferentes, no hay columnas comunes en los conjuntos de datos. Hemos intentado un par de enfoques, ambos enfoques no están dando resultado ...

Tengo dos clases que están estructuradas de esta manera: public class Company {private List <Person> person; ... public List <Person> getPerson () {return person; } ...

Estoy tratando de convertir la gramática ANTLR3 de ant a una gramática ANTLR4, para usarlo con el tiempo de ejecución antlr4-python2. Esta gramática es un analizador difuso C / C ++. Después de convertirlo (básicamente eliminando el árbol ...