Limpando uma lista de dados no Java8

11

Para limpar uma lista de dados, criei um método que aceita a lista de dados e a lista de operações de limpeza a serem executadas.

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

O problema aqui é que estamos criando a lista inteira novamente, pois Collectors.toList()retorna uma nova lista. Podemos alcançar o mesmo resultado sem usar o espaço extra?

Abaixo está o código para chamada:

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
fonte
toList()retorna um Collectornão um List, e não: você não pode ter "dados extra" sem "espaço extra"
xerx593

Respostas:

10

Se a modificação da lista no local for permitida, você poderá 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 duas Functioninstâncias e se pelo menos uma função cleanOpsestiver presente, ou seja, a lista não estiver vazia, a função combinada resultante será aplicada a todos os elementos da lista e os elementos substituídos pelo resultado, usando replaceAll.

Infelizmente, replaceAllrequer um UnaryOperator<T>e não um Function<T,T>, apesar de ser funcionalmente equivalente, por isso temos que usar o adaptador f::apply.

Como esses tipos de função são equivalentes, poderíamos alterar a lista para List<UnaryOperator<T>>, mas precisamos enfrentar o fato de que não há andThenimplementação especializada para UnaryOperator, portanto, precisaríamos 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;
}

A fonte do chamador muda para

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

então.

Como uma observação lateral, não há necessidade de uma construção como

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

como o toString()método de a Listproduz exatamente a mesma saída. Como o println(Object)método chama toString()implicitamente, você pode apenas usar

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

Parece que você precisa usar List.replaceAll(), que substitui cada elemento desta lista pelo resultado da aplicação do operador fornecido a esse 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;
}

Eu renomearia o método, já que é genérico, portanto não necessariamente processa um Listde Strings.

Eran
fonte