У нас есть список объектов доменной области (Person
). У объекта Person
есть 3 поля: id
, имя
, фамилия
. Задача: найти дубликаты и создать из них список (или множество, не важно), остальные объекты отбросить. Дубликатами являются объекты, у которых совпадает поле имя
. Реализовать механизм необходимо через стримы. Решение:
private static <T> Predicate<T> distinctByKey(Function<? super T, ?> keyExtractor) {
Set<Object> seen = new HashSet<>();
return t -> seen.add(keyExtractor.apply(t));
}
persons.stream().filter(distinctByKey(Person::getName))
Реализация работает, но есть вопросы по работе такого подхода:
HashSet
создается всего лишь единожды. Но почему? Изначально все же ожидается будто множество будет создаваться при каждой итерации. Хотелось бы увидеть развернутый ответ на этот счет.apply()
? Явно ведь я нигде не реализую Function
и метод apply()
соответственно.Java для каждой лямбды и ссылки на метод в момент выполнения создаёт прокси-класс, реализующий функциональный интерфейс. Метод distinctByKey
объявлен как принимающий функциональный интерфейс Function, поэтому при его вызове виртуальная машина создаст прокси-класс
class Example$$Lambda$1 implements Function<Person, String> {
public String apply(Person person) {
return person.getName();
}
}
и передаст его вместо ссылки на метод Person::getName
. Если убрать весь сахар, может стать понятнее, почему множество seen
создаётся один раз. Ваш код в рантайме преобразовывается в приблизительно эквивалентный этому:
class Example$$Lambda$2 implements Predicate<String> {
private final java.util.Set arg$1;
public boolean test(String name) {
return arg$1.add(name);
}
}
private static Example$$Lambda$2 distinctByKey(Example$$Lambda$1 keyExtractor) {
Set<Object> seen = new HashSet<>();
// Здесь seen "магическим" образом присваивается
// полю arg$1 возвращаемого объекта
return new Example$$Lambda$2();
}
Example$$Lambda$1 keyExtractor = new Example$$Lambda$1();
Example$$Lambda$2 predicate = distinctByKey(keyExtractor);
Iterator<Person> stream = persons.iterator();
List<String> result = new ArrayList<>();
while (stream.hasNext()) {
Person person = stream.next();
String key = keyExtractor.apply(person);
boolean duplicate = predicate.test(key);
if (!duplicate) {
result.add(key);
}
}
И кстати, операция filter оставляет в потоке элементы соответствующие предикату, а метод множества add возвращает true
для тех элементов, которых в множестве не было. То есть вы наоборот убираете дубликаты из потока. Вам надо инвертировать предикат:
persons.stream()
.filter(distinctByKey(Person::getName).negate());
Цель: Конвертировать список Api объектов в roomDataBase объекты и присваивать RecyclerViewAdapter уже roomDataBase объектамИ все это делать , не в основном потоке...
Когда тыкаю на Enter в EditText, выбирается "следующим" неправильное поле(
Загружаю в стартовой активности файл с сервера(~400КБ) Файл формата