Czyszczenie listy danych w Javie8

11

Do czyszczenia listy danych stworzyłem metodę, która akceptuje listę danych i listę operacji czyszczenia do wykonania.

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

Problem polega na tym, że ponownie tworzymy całą listę, ponieważ Collectors.toList()zwraca nową listę. Czy możemy osiągnąć ten sam wynik bez korzystania z dodatkowej przestrzeni?

Poniżej znajduje się kod wywołania:

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
źródło
toList()zwraca a Collectornie Listi nie: nie możesz mieć „dodatkowych danych” bez „dodatkowej przestrzeni”
xerx593

Odpowiedzi:

10

Jeśli modyfikowanie listy w miejscu jest dozwolone, możesz użyć

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

andThenłączy dwa Functionwystąpienia i jeśli obecna cleanOpsjest co najmniej jedna funkcja, tj. lista nie jest pusta, wynikowa połączona funkcja zostanie zastosowana do wszystkich elementów listy i elementów zastąpionych przez wynik, przy użyciu replaceAll.

Niestety replaceAllwymaga UnaryOperator<T>raczej niż Function<T,T>, mimo że jest funkcjonalnie równoważny, więc musimy użyć adaptera f::apply.

Ponieważ te typy funkcji są równoważne, moglibyśmy zmienić listę na List<UnaryOperator<T>>, ale wtedy musimy zmierzyć się z faktem, że nie ma specjalnej andThenimplementacji UnaryOperator, więc potrzebowalibyśmy:

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

Źródło dzwoniącego zmienia się na

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

następnie.

Na marginesie, nie ma takiej potrzeby

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

ponieważ toString()metoda a Listdaje dokładnie takie same wyniki. Ponieważ println(Object)metoda wywołuje toString()niejawnie, możesz po prostu użyć

System.out.println(cleanData(data, cleanOps));
Holger
źródło
7

Wygląda na to, że musisz użyć List.replaceAll(), który zastępuje każdy element tej listy wynikiem zastosowania danego operatora do tego elementu.

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

Chciałbym zmienić nazwę metody, chociaż, ponieważ jest to ogólny, więc nie musi przetwarzać Listz Strings.

Eran
źródło