Bereinigen einer Datenliste in Java8

11

Zum Bereinigen einer Datenliste habe ich eine Methode erstellt, die die Liste der Daten und die Liste der durchzuführenden Reinigungsvorgänge akzeptiert.

public <T> List<T> cleanData(List<T> data, List<Function<T, T>> cleanOps) {
    List<T>dataNew=data.stream().map((str) -> {
        T cleanData = str;
        for(Function<T,T> function:cleanOps) {
            cleanData=function.apply(cleanData);
        }
        return cleanData;
    }).collect(Collectors.toList());
    return dataNew;
}

Das Problem hierbei ist, dass wir die gesamte Liste erneut erstellen, da Collectors.toList()eine neue Liste zurückgegeben wird. Können wir das gleiche Ergebnis erzielen, ohne den zusätzlichen Platz zu nutzen?

Unten ist der Code für den Aufruf:

public void processData() {
    List<Function<String, String>> cleanOps = new ArrayList<>();
    cleanOps.add(String::toLowerCase);
    cleanOps.add(str -> str.replaceAll(" ", ""));
    List<String> data = new ArrayList<>();
    data.add("John Doe");
    data.add("Jane Doe");
    System.out.println(Arrays.toString(cleanData(data, cleanOps).toArray()));
}
Dharmvir Tiwari
quelle
toList()gibt ein Collectornicht ein Listund nein zurück: Sie können keine "zusätzlichen Daten" ohne "zusätzlichen Speicherplatz" haben
xerx593

Antworten:

10

Wenn das Ändern der Liste an Ort und Stelle zulässig ist, können Sie verwenden

public <T> List<T> cleanData(List<T> data, List<Function<T, T>> cleanOps) {
    cleanOps.stream().reduce(Function::andThen).ifPresent(f -> data.replaceAll(f::apply));
    return data;
}

andThenkombiniert zwei FunctionInstanzen und wenn mindestens eine Funktion vorhanden war, dh die cleanOpsListe nicht leer ist, wird die resultierende kombinierte Funktion auf alle Listenelemente angewendet und die durch das Ergebnis ersetzten Elemente mit replaceAll.

Leider replaceAllerfordert ein UnaryOperator<T>eher als ein Function<T,T>, obwohl es funktional gleichwertig ist, so dass wir den Adapter verwenden müssen f::apply.

Da diese Funktionstypen äquivalent sind, könnten wir die Liste in ändern List<UnaryOperator<T>>, aber dann müssen wir uns der Tatsache stellen, dass es keine spezielle andThenImplementierung für gibt UnaryOperator, also müssten wir:

public <T> List<T> cleanData(List<T> data, List<UnaryOperator<T>> cleanOps) {
    cleanOps.stream()
        .reduce((f1,f2) -> t -> f2.apply(f1.apply(t)))
        .ifPresent(data::replaceAll);
    return data;
}

Die Quelle des Anrufers ändert sich zu

List<UnaryOperator<String>> cleanOps = new ArrayList<>();
cleanOps.add(String::toLowerCase);
cleanOps.add(str -> str.replaceAll(" ", ""));
List<String> data = new ArrayList<>();
data.add("John Doe");
data.add("Jane Doe");
System.out.println(cleanData(data, cleanOps));

dann.

Nebenbei bemerkt, es besteht keine Notwendigkeit für ein Konstrukt wie

System.out.println(Arrays.toString(cleanData(data, cleanOps).toArray()));

da die toString()Methode von a Listgenau die gleiche Ausgabe erzeugt. Da die println(Object)Methode toString()implizit aufruft , können Sie einfach verwenden

System.out.println(cleanData(data, cleanOps));
Holger
quelle
7

Es sieht so aus, als müssten Sie verwenden List.replaceAll(), wodurch jedes Element dieser Liste durch das Ergebnis der Anwendung des angegebenen Operators auf dieses Element ersetzt wird.

public <T> List<T> cleanString(List<T> data, List<Function<T, T>> cleanOps) {
    data.replaceAll(str -> {
        T cleanData = str;
        for (Function<T,T> function : cleanOps) {
            cleanData = function.apply(cleanData);
        }
        return cleanData;
    });
    return data;
}

Ich würde die Methode jedoch umbenennen, da sie generisch ist und daher nicht unbedingt ein Listvon Strings verarbeitet.

Eran
quelle