Очистка списка данных в Java8

11

Для очистки списка данных я создал метод, который принимает список данных и список операций очистки, которые необходимо выполнить.

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

Проблема в том, что мы снова создаем весь список, так как он Collectors.toList()возвращает новый список. Можем ли мы достичь того же результата без использования дополнительного пространства?

Ниже приведен код для вызова:

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()));
}
Дхармвир Тивари
источник
toList()возвращает Collectornot, a Listи no: вы не можете иметь «лишние данные» без «лишнего пробела»
xerx593

Ответы:

10

Если изменение списка на месте разрешено, вы можете использовать

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объединяет два Functionэкземпляра и, если хотя бы одна функция присутствовала, т.е. cleanOpsсписок не пуст, результирующая объединенная функция будет применена ко всем элементам списка и элементам, замененным результатом, используя replaceAll.

К сожалению, replaceAllтребуется, UnaryOperator<T>а не Function<T,T>, несмотря на то, что функционально эквивалентны, поэтому мы должны использовать адаптер f::apply.

Поскольку эти типы функций эквивалентны, мы могли бы изменить список на List<UnaryOperator<T>>, но тогда мы должны столкнуться с тем фактом, что не существует специализированной andThenреализации для UnaryOperator, поэтому нам потребуется:

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

Источник звонка меняется на

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

тогда.

В качестве примечания, нет необходимости в такой конструкции, как

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

так как toString()метод Listпроизводит точно такой же результат. Поскольку println(Object)метод вызывает toString()неявно, вы можете просто использовать

System.out.println(cleanData(data, cleanOps));
Holger
источник
7

Похоже, вам нужно использовать List.replaceAll(), который заменяет каждый элемент этого списка с результатом применения данного оператора к этому элементу.

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

Однако я бы переименовал метод, поскольку он универсальный, поэтому не обязательно обрабатывает a Listof Strings.

Эран
источник