Limpieza de una lista de datos en Java8

11

Para limpiar una lista de datos, he creado un método que acepta la lista de datos y la lista de operaciones de limpieza a realizar.

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

El problema aquí es que estamos creando toda la lista nuevamente, ya que Collectors.toList()devuelve una nueva lista. ¿Podemos lograr el mismo resultado sin usar el espacio extra?

A continuación se muestra el código de invocación:

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
fuente
toList()devuelve un Collectorno a List, y no: no puede tener "datos adicionales" sin "espacio adicional"
xerx593

Respuestas:

10

Si se permite modificar la lista en el lugar, puede usar

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

andThencombina dos Functioninstancias y si al menos una función estaba presente, es decir, la cleanOpslista no está vacía, la función combinada resultante se aplicará a todos los elementos de la lista y los elementos reemplazados por el resultado, utilizando replaceAll.

Desafortunadamente, replaceAllrequiere un en UnaryOperator<T>lugar de un Function<T,T>, a pesar de ser funcionalmente equivalente, por lo que tenemos que usar el adaptador f::apply.

Dado que estos tipos de funciones son equivalentes, podríamos cambiar la lista a List<UnaryOperator<T>>, pero luego, tenemos que enfrentar el hecho de que no hay una andThenimplementación especializada UnaryOperator, por lo que necesitaríamos:

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 fuente de la persona que llama cambia a

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

entonces.

Como nota al margen, no hay necesidad de una construcción como

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

como el toString()método de a Listproduce exactamente la misma salida. Como el println(Object)método llama toString()implícitamente, solo puede usar

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

Parece que necesita usar List.replaceAll(), que reemplaza cada elemento de esta lista con el resultado de aplicar el operador dado a ese elemento.

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

Sin embargo, cambiaría el nombre del método, ya que es genérico, por lo que no necesariamente procesa una Listde Strings.

Eran
fuente