Nettoyage d'une liste de données en Java8

11

Pour nettoyer une liste de données, j'ai créé une méthode qui accepte la liste des données et la liste des opérations de nettoyage à effectuer.

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

Le problème ici est que nous créons à nouveau la liste entière comme Collectors.toList()renvoie une nouvelle liste. Pouvons-nous obtenir le même résultat sans utiliser l'espace supplémentaire?

Voici le code d'invocation:

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
la source
toList()renvoie un Collectorpas un List, et non: vous ne pouvez pas avoir de "données supplémentaires" sans "espace supplémentaire"
xerx593

Réponses:

10

Si la modification de la liste sur place est autorisée, vous pouvez utiliser

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

andThencombine deux Functioninstances et si au moins une fonction était présente, c'est-à-dire que la cleanOpsliste n'est pas vide, la fonction combinée résultante sera appliquée à tous les éléments de la liste et aux éléments remplacés par le résultat, en utilisant replaceAll.

Malheureusement, replaceAllnécessite un UnaryOperator<T>plutôt qu'un Function<T,T>, bien qu'il soit fonctionnellement équivalent, nous devons donc utiliser l'adaptateur f::apply.

Étant donné que ces types de fonctions sont équivalents, nous pourrions changer la liste en List<UnaryOperator<T>>, mais nous devons faire face au fait qu'il n'y a pas d' andThenimplémentation spécialisée pour UnaryOperator, nous aurions donc besoin de:

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

La source de l'appelant devient

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

puis.

En remarque, il n'y a pas besoin d'une construction comme

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

car la toString()méthode de a Listproduit exactement la même sortie. Comme la println(Object)méthode appelle toString()implicitement, vous pouvez simplement utiliser

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

Il semble que vous deviez utiliser List.replaceAll(), ce qui remplace chaque élément de cette liste par le résultat de l'application de l'opérateur donné à cet élément.

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

Je renommerais la méthode, cependant, car elle est générique, donc elle ne traite pas nécessairement l'une Listdes Strings.

Eran
la source