Contando múltiples campos de un objeto usando flujos

Me preguntaba cómo contar diferentes campos de un objeto usando una sola secuencia. Sé que puedo contar fácilmente una sola propiedad de un objeto usando streams ( countedWithStream) o incluso usando un para contar varias a la vez ( countedWithFor). Pero en realidad me encantaría saber si sería posible lograr lo mismo countedWithForpero utilizando una sola secuencia, generando la misma salida.

import com.google.common.collect.ImmutableMap;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.stream.LongStream;

import static java.util.stream.Collectors.*;

class Scratch {
public static void main(String[] args) {

    List<AnObject> objects = createObjects();

    Map<String, Map<Long, Long>> countedWithStream = countUsingStream(objects);
    Map<String, Map<Long, Long>> countedWithFor = countUsingFor(objects);
}

private static Map<String, Map<Long, Long>> countUsingStream(List<AnObject> objects) {
    BiFunction<List<AnObject>, Function<AnObject, Long>, Map<Long, Long>> count = (ojs, mpr) -> ojs.stream()
                                                                                                   .collect(groupingBy(mpr, counting()));

    return ImmutableMap.<String, Map<Long, Long>>builder().put("firstId", count.apply(objects, AnObject::getFirstId))
                                                          .put("secondId", count.apply(objects, AnObject::getSecondId))
                                                          .build();
}
private static Map<String, Map<Long, Long>> countUsingFor(List<AnObject> objects) {
    Map<Long, Long> firstIdMap = new HashMap<>();
    Map<Long, Long> secondIdMap = new HashMap<>();

    final BiFunction<Long, Map<Long, Long>, Long> count = (k, m) -> k != null ? m.containsKey(k) ? m.put(k, m.get(k) + 1L) : m.put(k, 1L) : null;

    for (AnObject object : objects) {
        count.apply(object.firstId, firstIdMap);
        count.apply(object.secondId, secondIdMap);
    }

    return ImmutableMap.<String, Map<Long, Long>>builder().put("firstId", firstIdMap)
                                                          .put("secondId", secondIdMap)
                                                          .build();
}

private static List<AnObject> createObjects() {
    return LongStream.range(1, 11)
                     .mapToObj(Scratch::createObject)
                     .collect(toList());
}

private static AnObject createObject(long id) {
    return new AnObject(id, id);
}

private static class AnObject {
    public final long firstId;
    public final long secondId;

    public AnObject(long firstId, 
                    long secondId) {
        this.firstId = firstId;
        this.secondId = secondId;
    }

    public long getFirstId() {
        return firstId;
    }

    public long getSecondId() {
        return secondId;
    }
}
Respuesta 1

Puede usar una reducción para hacer el trabajo en n iteraciones con algo como esto:

Supplier<Map<String, Map<Long, Long>>> mapSupplier = () -> {
    Map<String, Map<Long, Long>> outputMap = new HashMap<>();
    outputMap.put("firstId", new HashMap<>());
    outputMap.put("secondId", new HashMap<>());
    return outputMap;
};

Map<String, Map<Long, Long>> reduce = objects.stream().collect(mapSupplier,
        (acc, obj) -> {
            acc.get("firstId").merge(obj.firstId, 1L, (curv, incr) -> curv + incr);
            acc.get("secondId").merge(obj.secondId, 1L, (curv, incr) -> curv + incr);
        }
        , (acc1, acc2) -> {
            acc2.get("firstId").forEach((k, v) -> acc1.get("firstId").merge(k, v, (v1, v2) -> v1 + v2));
            acc2.get("secondId").forEach((k, v) -> acc1.get("secondId").merge(k, v, (v1, v2) -> v1 + v2));
        });

Pero esto puede no ser tan conciso como quieres que sea.

Respuesta: 2

Dado que tengo una tabla en la que cierto campo tiene un tipo de datos de texto, digamos que FieldValue podría dar texto como tipo de datos. Estoy guardando un objeto de fecha de Java usando el formato completo y guardando esto en el ...

Necesito una forma de convertir las oraciones en solo una cadena de letras minúsculas, es decir, sin espacios, apóstrofes ni nada mientras uso un bucle. String estático toAlphaLowerCase (String s) {String c; ...

Tengo antecedentes de C ++. En algún lugar en el medio de mi proyecto de Java necesito definir una variable cuyo tipo depende de un valor booleano (que había definido anteriormente) ¿puedo hacer esto: if (Main.NEW_STYLE) ...

Utilizo el servidor jetty para manejar las solicitudes de los clientes y tengo un requisito en el que necesito iniciar o detener uno o pocos ServerConnectors a pedido sin reiniciar el servidor al que están estos conectores ...