solución más eficiente en la codificación de tareas utilizando la API Stream?

étrico:

Recientemente tuve una entrevista técnica y tengo tarea de codificación de pequeña API Stream.
Consideremos siguiente entrada:

public class Student {
    private String name;
    private List<String> subjects;
    //getters and setters
}

Student stud1 = new Student("John", Arrays.asList("Math", "Chemistry"));
Student stud2 = new Student("Peter", Arrays.asList("Math", "History"));
Student stud3 = new Student("Antony", Arrays.asList("Music", "History", "English"));

Stream<Student> studentStream = Stream.of(stud1, stud2, stud3);

La tarea es encontrar estudiantes con temas únicos que utilizan la API de corriente .
Así que por el resultado esperado provisto de entrada (orden de ignorar) es [John, Anthony].

Presenté la solución utilizando colector personalizado:

Collector<Student, Map<String, Set<String>>, List<String>> studentsCollector = Collector.of(
        HashMap::new,
        (container, student) -> student.getSubjects().forEach(
                subject -> container
                        .computeIfAbsent(subject, s -> new HashSet<>())
                        .add(student.getName())),
        (c1, c2) -> c1,
        container -> container.entrySet().stream()
                .filter(e -> e.getValue().size() == 1)
                .map(e -> e.getValue().iterator().next())
                .distinct()
                .collect(Collectors.toList())
);
List<String> studentNames = studentStream.collect(studentsCollector);

Pero la solución se considera como no óptima / eficiente.
Podría, por favor comparta sus ideas sobre la solución más eficiente para esta tarea?

ACTUALIZACIÓN: Tengo otra opinión de un chico que iba a usar (método Stream.reduce ()) reductor. Pero no puedo entender cómo esto podría aumentar la eficiencia. ¿Qué piensas?

MC emperador:

Aquí es otra.

// using SimpleEntry from java.util.AbstractMap
Set<Student> list = new HashSet<>(studentStream
    .flatMap(student -> student.getSubjects().stream()
        .map(subject -> new SimpleEntry<>(subject, student)))
    .collect(Collectors.toMap(Entry::getKey, Entry::getValue, (l, r) -> Student.SENTINEL_VALUE)
    .values());
list.remove(Student.SENTINEL_VALUE);

(Intencionalmente utilizando un valor centinela, más sobre esto más adelante).

Los pasos:

  1. Set<Student> list = new HashSet<>(studentStream
    

    Estamos creando un HashSet de la colección que vamos a recoger. Esto se debe a que queremos para deshacerse de los estudiantes duplicados (estudiantes con múltiples sujetos singulares, en su caso Antony).

  2. .flatMap(student -> student.subjects()
        .map(subject -> new SimpleEntry(subject, student)))
    

    Estamos flatmapping temas de cada estudiante en una corriente, pero primero asignar cada elemento de un par con elementos como la clave del tema y como valor el estudiante. Esto se debe a que necesitamos para retener la asociación entre el sujeto y el estudiante. Estoy usando AbstractMap.SimpleEntry, pero, por supuesto, se puede utilizar cualquier aplicación de un par.

  3. .collect(Collectors.toMap(Entry::getKey, Entry::getValue, (l, r) -> Student.SENTINEL_VALUE)
    

    Estamos recogiendo los valores en un mapa, ponga el asunto como la clave y el estudiante como valor para el mapa resultante. Pasamos en un tercer argumento (a BinaryOperator) para definir lo que debería ocurrir si una colisión de llaves se realiza. No podemos pasar null, por lo que utilizar un valor centinela 1 .
    En este punto, hemos invertido el estudiante relación ↔ sujeto mediante la asignación de cada sujeto a un estudiante (o el SENTINEL_VALUEsi un sujeto tiene varios estudiantes).

  4. .values());
    

    Tomamos los valores del mapa, dando la lista de todos los estudiantes con un único tema, más el valor centinela.

  5. list.remove(Student.SENTINEL_VALUE);
    

    Lo único que queda por hacer es deshacerse del valor centinela.


1 No podemos utilizar nullen esta situación. La mayoría de las implementaciones de un mapa no hacen distinción entre una clave asignada a nullo la ausencia de esa clave en particular. O, más exactamente, el método de combinación de HashMap activamente elimina un nodo cuando la reasignación devuelve la función null. Si queremos evitar un valor centinela, entonces debemos poner en práctica o el propio mergemétodo, que podría aplicarse a algo como esto: return (!containsKey(key) ? super.merge(key, value, remappingFunction) : put(key, null));.

Supongo que te gusta

Origin http://43.154.161.224:23101/article/api/json?id=167956&siteId=1
Recomendado
Clasificación