Как сгруппировать по свойству объекта в списке Java? - PullRequest
0 голосов
/ 06 июня 2018

У меня есть список объектов ресурса.Ресурсный объект определяется как:

class Resource {
    String ip;
    String subnet;
    // other properties...
}

Мне нужен набор данных, подобный карте, где ключ - это случайный ресурс в одной подсети, а значение - список всех ресурсов в других подсетях.

Например, если S обозначает подсеть, а R обозначает ресурс, список содержит:

S1R1, S1R2, S2R1, S2R2, S3R1 and S3R2

Ожидаемый результат:

S1R1 -> S2R1, S2R2, S3R1, S3R2
S2R2 -> S1R1, S1R2, S3R1, S3R2
S3R1 -> S1R1, S1R2, S2R1, S2R2

Я попытался использовать поток и собрать сгруппированныена основе подсетей, не знаю, как действовать дальше.

Map<String, Set<Resource>> grouped = resourceList.stream()
    .collect(Collectors.groupingBy(Resource::getSubnet, Collectors.toSet()));

Ответы [ 2 ]

0 голосов
/ 06 июня 2018

Это не работает с коллектором groupingBy, так как он работает, передавая элементы, принадлежащие группе, нижестоящему коллектору каждой группы, и вы не можете сказать ему пропускать другие.

Oneспособ достижения того, что вы описали, это

TreeMap<Resource, Set<Resource>> map
                              = new TreeMap<>(Comparator.comparing(Resource::getSubnet));
for(Resource resource: resourceList)
    map.computeIfAbsent(resource, x -> new HashSet<>(resourceList)).remove(resource);

map.forEach((resource,set) -> System.out.println(resource+" -> "
    +set.stream().map(Resource::toString).collect(Collectors.joining(", "))));

, который, учитывая список S1R1, S1R2, S2R1, S2R2, S3R1, S3R2, произведет

S1R1 -> S3R2, S2R1, S2R2, S3R1
S2R1 -> S3R2, S1R2, S3R1, S1R1
S3R1 -> S1R2, S2R1, S2R2, S1R1

, тогда как порядок элементов Set равенundefined, ключи Map фактически не выбираются случайным образом, это первый встреченный ресурс каждой подсети.

Это можно сделать с помощью двух операций цепочки потоков

Map<Resource, Set<Resource>> map = resourceList.stream()
    .collect(Collectors.groupingBy(Resource::getSubnet, Collectors.toSet()))
    .values().stream()
    .collect(Collectors.toMap(
        set -> set.iterator().next(),
        set -> {
            Set<Resource> other = new HashSet<>(resourceList);
            other.removeAll(set);
            return other;
        }));

Здесьресурс, выбранный из каждой группы, действительно не указан.В отличие от подхода, основанного на TreeMap, вам нужно знать фактический Resource (или попробовать их все), чтобы найти группу.TreeMap позволяет искать группу, используя любой Resource из той же подсети.

0 голосов
/ 06 июня 2018

Я не уверен, как работают подсети, но вы должны выяснить следующее соотношение ( isSubNetOf ).

BiPredicate<Resource, Resource> isSubNetOf = (parent, subnet) -> ???;

Map<Resource, Set<Resource>> resourceToSubnets = resourceList.stream()
        .collect(toMap(
                Function.identity(),
                x -> resourceList.stream().filter(v -> isSubNetOf.test(x, v)).collect(toSet())
                ));

Если вы хотите Map<String, Set<Resource>> изменить Function.identity()до необходимого Resource::getIp или Resource::getSubnet.

...