упростить java поток, чтобы найти повторяющиеся свойства - PullRequest
4 голосов
/ 03 апреля 2020

У меня есть список users, и я хочу найти всех пользователей с повторяющимися именами:

var allNames = users
              .stream()
              .map(u -> u.getName()).collect(Collectors.toList());

var duplicateNames = allNames
                .stream()
                .filter(i -> Collections.frequency(allNames, i) > 1)
                .collect(Collectors.toSet());

Можно ли улучшить / упростить вышеуказанное решение?

Например, на самом деле я создаю список со всеми именами, а затем фильтрую его. Как я могу просмотреть список, чтобы найти его дубликаты, не создавая дополнительный список allNames?

Ответы [ 3 ]

7 голосов
/ 03 апреля 2020

Одним из решений является

var duplicate = users.stream()
    .collect(Collectors.toMap(User::getName, u -> false, (x,y) -> true))
    .entrySet().stream()
    .filter(Map.Entry::getValue)
    .map(Map.Entry::getKey)
    .collect(Collectors.toSet());

Это создает промежуточный Map<String,Boolean> для записи, какое имя встречается более одного раза. Вы можете использовать keySet() этой карты вместо сбора в новое Set:

var duplicate = users.stream()
    .collect(Collectors.collectingAndThen(
        Collectors.toMap(User::getName, u -> false, (x,y) -> true, HashMap::new),
            m -> {
                m.values().removeIf(dup -> !dup);
                return m.keySet();
            }));

Решение l oop может быть намного проще:

HashSet<String> seen = new HashSet<>(), duplicate = new HashSet<>();
for(User u: users)
    if(!seen.add(u.getName())) duplicate.add(u.getName());
3 голосов
/ 03 апреля 2020

Сгруппируйте по именам, найдите записи с более чем одним значением:

Map<String, List<User>> grouped = users.stream()
    .collect(groupingBy(User::getName));

List<User> duplicated =
    grouped.values().stream()
        .filter(v -> v.size() > 1)
        .flatMap(List::stream)
        .collect(toList());

(Вы можете сделать это в одном выражении, если хотите. Я только разделил шаги, чтобы сделать его немного более понятным что происходит).

Обратите внимание, что это не сохраняет порядок пользователей из исходного списка.

1 голос
/ 03 апреля 2020

Я нахожу решение с помощью @holger:

// collect all duplicate names with O(n)
var duplicateNames = all.stream()
                .collect(Collectors.groupingBy(Strategy::getName, Collectors.counting()))
                .entrySet()
                .stream()
                .filter(m -> m.getValue() > 1)
                .map(m -> m.getKey())
                .collect(Collectors.toList());

Производительность этого решения O (n ^ 2) или O (n)?

Если кто-то может найди улучшения тогда поделись пожалуйста.

...