Как удалить элементы из списка с лямбдой на основе другого списка
У меня есть список путей к файлу:.
List<Path> filePaths; //e.g. [src\test\resources\file\15\54\54_exampleFile.pdf]
54
выше относится к идентификатору файла
Затем я получаю a Set
идентификаторов String
, которые мое приложение может обрабатывать следующим образом:
Set<String> acceptedIds = connection.getAcceptedIDs(); //e.g. elements [64, 101, 33]
Как я могу использовать Java 8 lambdas для filter
всех элементов в filePaths
, которые не содержат никаких приемлемых идентификаторов, которые содержатся в наборе коллекции acceptedIds
.
Другими словами, я хотел бы сохранить в filePaths
только те пути, которые имеют идентификаторы, которые находятся в acceptedIds
. Например, 54 не находится в указанном выше списке, поэтому он удаляется.
filePaths.stream().filter(...).collect(Collectors.toList());
Ответы
Ответ 1
Самый эффективный способ - извлечь идентификатор из пути, затем попытаться найти его в Set, чтобы каждый фильтр выполнялся в постоянное время, т.е. O(1)
, давая общий O(n)
, где n
- это количество путей:
filePaths.stream()
.filter(p -> acceptedIds.contains(p.getParent().getFileName().toString()))
.collect(Collectors.toList());
Если обратный подход выполняется, где каждый acceptedIds
выполняется поиск в пути (как и в других ответах), каждый фильтр имеет значение O(m*k)
, где m
- это число acceptedIds
и k
средняя длина пути, дающая общий O(n * m * k)
, который будет работать очень плохо для даже умеренных размеров коллекций.
Ответ 2
Вы можете написать:
filePaths.stream()
.filter(p -> acceptedIds.stream().anyMatch(id -> p.toString().contains(id)))
.collect(toList());
Это фильтрует каждый путь таким образом, что хотя бы один из acceptedIds
содержится в строковом представлении пути. Возможно, вы захотите реализовать что-то лучше, чем contains
здесь, в зависимости от вашего прецедента (например, для соответствия имени файла).
anyMatch
- это операция, которая определяет, соответствует ли хотя бы один элемент указанному предикату.
Обратите внимание, что в этом ответе не делается никаких предположений о пути фильтрации элементов. Если вы можете с уверенностью сказать, что в каждом пути родительский каталог имеет имя с идентификатором, вы обязательно должны пойти с ответом @Bohemian по причине производительности.
Ответ 3
Так же:
List removeMissing(List l1, List l2) {
List ret = l1.stream()
.filter(o -> l2.contains(o)) //Keep if object o satisfies the condition "l2 contains a reference to this object"
.collect(Collectors.toList());
return ret;
}
Ответ 4
Если ваша структура имен файлов постоянна, я сначала использую регулярное выражение для извлечения числа, а затем проверяет, находится ли он в числе желаемых идентификаторов.
final Set<String> acceptedIds = ...
// Matches the number of the file, concluded with the underscore
final Pattern extractor = Pattern.compile("\.*(?<number>\d+)_")
filePaths.stream().filter( path -> {
final Matcher m = extractor
.matcher(path.getFileName().toString());
m.find();
return acceptedIds.contains(m.group("number"));
})
...