Переделать список с некоторым условием - PullRequest
0 голосов
/ 03 октября 2018

Существует два объекта:

class GiftCertificate {
    Long id;
    List<Tag> tags;
}

class Tag {
   Long id;
   String name;
}

Существует список

List<GiftCertificate> 

, который содержит, например, следующие данные: <1, [1, "Tag1"]>, <2, null>, <1, [2, "Tag2"]>.(Он не содержит набор тегов, но содержит только один тег или не имеет его вообще).

Мне нужно сделать так, чтобы в результате это было так: <1, {[1," Tag1 "], [2," Tag2 "]}>, <2, null>.Я имею в виду, добавить в набор первого объекта тег из третьего GiftCertificate и в то же время удалить третий.Я хотел бы получить хотя бы некоторые идеи о том, как это сделать.было бы неплохо использовать стрим.

Ответы [ 4 ]

0 голосов
/ 05 октября 2018

Java 9 представил flatMapping коллектор, который особенно хорошо подходит для подобных задач.Разбейте задачу на два шага.Сначала создайте карту идентификаторов подарочных сертификатов в списке тегов, а затем создайте новый список GiftCertificate объектов:

import static java.util.stream.Collectors.flatMapping;
import static java.util.stream.Collectors.groupingBy;
import static java.util.stream.Collectors.toList;
......

Map<Long, List<Tag>> gcIdToTags = gcs.stream()
    .collect(groupingBy(
        GiftCertificate::getId,
        flatMapping(
            gc -> gc.getTags() == null ? Stream.empty() : gc.getTags().stream(),
            toList()
        )
    ));

List<GiftCertificate> r = gcIdToTags.entrySet().stream()
    .map(e -> new GiftCertificate(e.getKey(), e.getValue()))
    .collect(toList());

. Предполагается, что GiftCertificate имеет конструктор, который принимает Long id и List<Tag> tags

Обратите внимание, что этот код отличается от ваших требований, создавая пустой список вместо null в случае, если нет тегов для идентификатора подарочного сертификата.Использование null вместо пустого списка - очень паршивый дизайн, заставляющий вас загрязнять ваш код проверками null повсюду.

Первый аргумент flatMapping также можно записать как gc -> Stream.ofNullable(gc.getTags()).flatMap(List::stream), если вы найдете его более читабельным.

0 голосов
/ 04 октября 2018

Это можно сделать с помощью Java8, например,

final List<GiftCertificate> mergedGiftCerts = giftCerts.stream()
        .collect(Collectors.collectingAndThen(
                Collectors.groupingBy(GiftCertificate::getId,
                        Collectors.mapping(gc -> gc.getTags() != null ? gc.getTags().get(0) : null,
                                Collectors.toList())),
                m -> m.entrySet().stream().map(e -> new GiftCertificate(e.getKey(), e.getValue()))
                        .collect(Collectors.toList())));
0 голосов
/ 04 октября 2018

Вы можете делать что угодно с потоками и с помощью специального пользовательского конструктора и нескольких вспомогательных методов в GiftCertificate.Вот конструктор:

public GiftCertificate(GiftCertificate another) {
    this.id = another.id;
    this.tags = new ArrayList<>(another.tags);
}

Это просто работает как конструктор копирования.Мы создаем новый список тегов, поэтому, если список тегов одного из экземпляров GiftCertificate будет изменен, другой не будет изменен.(Это просто основные концепции ОО: инкапсуляция).

Затем, чтобы добавить другие теги GiftCertificate в этот список тегов GiftCertificate, вы можете добавить следующий метод к GiftCertificate:

public GiftCertificate addTagsFrom(GiftCertificate another) {
    tags.addAll(another.tags);
    return this;
}

А также вспомогательный метод, который возвращает, является ли список тегов пустым или нет, очень пригодится:

public boolean hasTags() {
    return tags != null && !tags.isEmpty();
}

Наконец, с этими тремя простыми методамина месте, мы готовы использовать все возможности потоков для элегантного решения проблемы:

Collection<GiftCertificate> result = certificates.stream()
    .filter(GiftCertificate::hasTags)  // keep only gift certificates with tags
    .collect(Collectors.toMap(
        GiftCertificate::getId,        // group by id
        GiftCertificate::new,          // use our dedicated constructor
        GiftCertificate::addTagsFrom)) // merge the tags here
    .values();

При этом используется Collectors.toMap для создания карты, которая группирует подарочные сертификаты по id,слияние тегов.Затем мы сохраняем значения карты.


Вот эквивалентное решение без потоков:

Map<Long, GiftCertificate> map = new LinkedHashMap<>(); // preserves insertion order
certificates.forEach(cert -> {
    if (cert.hasTags()) {
        map.merge(
            cert.getId(), 
            new GiftCertificate(cert), 
            GiftCertificate::addTagsFrom);
    }
});

Collection<GiftCertificate> result = map.values();

А вот вариант с небольшим улучшением производительности:

Map<Long, GiftCertificate> map = new LinkedHashMap<>(); // preserves insertion order
certificates.forEach(cert -> {
    if (cert.hasTags()) {
        map.computeIfAbsent(
                cert.getId(), 
                k -> new GiftCertificate(k)) // or GitCertificate::new
            .addTagsFrom(cert);
    }
});

Collection<GiftCertificate> result = map.values();

Для этого решения требуется следующий конструктор:

public GiftCertificate(Long id) {
    this.id = id;
    this.tags = new ArrayList<>();
}

Преимущество этого подхода состоит в том, что новые GiftCertificate экземпляры будут создаваться только в том случае, если на карте нет другой записи стот же идентификатор.

0 голосов
/ 03 октября 2018

Вероятно, не самый эффективный способ, но он может помочь

private List<GiftCertificate> joinCertificates(List<GiftCertificate> giftCertificates) {
    return giftCertificates.stream()
            .collect(Collectors.groupingBy(GiftCertificate::getId))
            .entrySet().stream()
            .map(entry -> new GiftCertificate(entry.getKey(), joinTags(entry.getValue()))).collect(Collectors.toList());
}

private List<Tag> joinTags(List<GiftCertificate> giftCertificates) {
    return giftCertificates.stream()
            .flatMap(giftCertificate -> Optional.ofNullable(giftCertificate.getTags()).stream().flatMap(Collection::stream))
            .collect(Collectors.toList());
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...