Как объединить два списка POJOS, используя поток JAVA 8, изменяя некоторые значения при необходимости - PullRequest
3 голосов
/ 29 марта 2019

Надеюсь, ты сможешь мне помочь.Я постараюсь прояснить название вопроса.У меня есть один POJO, подобный этому

public class Factor {
    private int id;
    private String item;
    private int unitId;
    private Double factor;

    // constructor getters and setters
}

У меня есть два List этого класса с данными из двух разных таблиц в двух разных БД.

Метод equals определяется следующим образом

import com.google.common.base.Objects;
//....
@Override
public boolean equals( Object o ) {
    if ( this == o )
        return true;
    if ( o == null || getClass() != o.getClass() )
        return false;
    Factor that = (Factor) o;
    return getUnitId() == that.getUnitId() &&
               Objects.equal(getItem(), that.getItem());
}

, поэтому два объекта считаются равными, если они имеют одинаковые атрибуты item и unitId.

Допустим, у меня есть два списка:

List<Factor> listA = new ArrayList<>();
listA.add(new Factor(1, "Item1", 1, 0.5));
listA.add(new Factor(2, "Item1", 2, 0.6));
listA.add(new Factor(3, "Item2", 1, 1.0));
listA.add(new Factor(4, "Item3", 1, 2.0));

List<Factor> listB = new ArrayList<>();
listB.add(new Factor(0, "Item1", 1, 0.8));
listB.add(new Factor(0, "Item1", 2, 0.9));
listB.add(new Factor(0, "Item4", 1, 1.0));
listB.add(new Factor(0, "Item5", 1, 2.0));

Принимая во внимание метод equals, первые два элемента списков следует считать равными.

Причина, по которой ids в списке B все 0, потому что в этой таблице нет поля id.

Я пытаюсь создать новый список, объединив эти два списка, но еслиобъекты равны, это должно взять id из listA и коэффициент из listB.

Таким образом, список результатов будет иметь следующие объекты

(1, "Item1", 1, 0.8)
(2, "Item1", 2, 0.9)
(0, "Item4", 1, 1.0)
(0, "Item5", 1, 2.0)

Я знаю, как сравнить оба списка с stream и отфильтровать, какие объекты равны или различны, но кто-нибудь знает, как получить новый объект, объединяющий равные?

Ответы [ 2 ]

1 голос
/ 29 марта 2019

Вы ищете функцию "zip".Однако в Stream API его нет (по крайней мере, в Java 8, я не знаю о более новых версиях).

Вы можете найти, как написать zip метод самостоятельно в этот пост .

Используя ответ siki, мы получаем этот метод:

public static<A, B, C> Stream<C> zip(Stream<? extends A> a,
                                     Stream<? extends B> b,
                                     BiFunction<? super A, ? super B, ? extends C> zipper) {
    Objects.requireNonNull(zipper);
    Spliterator<? extends A> aSpliterator = Objects.requireNonNull(a).spliterator();
    Spliterator<? extends B> bSpliterator = Objects.requireNonNull(b).spliterator();

    // Zipping looses DISTINCT and SORTED characteristics
    int characteristics = aSpliterator.characteristics() & bSpliterator.characteristics() &
            ~(Spliterator.DISTINCT | Spliterator.SORTED);

    long zipSize = ((characteristics & Spliterator.SIZED) != 0)
            ? Math.min(aSpliterator.getExactSizeIfKnown(), bSpliterator.getExactSizeIfKnown())
            : -1;

    Iterator<A> aIterator = Spliterators.iterator(aSpliterator);
    Iterator<B> bIterator = Spliterators.iterator(bSpliterator);
    Iterator<C> cIterator = new Iterator<C>() {
        @Override
        public boolean hasNext() {
            return aIterator.hasNext() && bIterator.hasNext();
        }

        @Override
        public C next() {
            return zipper.apply(aIterator.next(), bIterator.next());
        }
    };

    Spliterator<C> split = Spliterators.spliterator(cIterator, zipSize, characteristics);
    return (a.isParallel() || b.isParallel())
            ? StreamSupport.stream(split, true)
            : StreamSupport.stream(split, false);
}

Мы можем использовать его так:

List<Factor> result = zip(list1.stream(), list2.stream(),
        (x, y) -> x.equals(y) ? new Factor(x.getId(), y.getItem(), y.getUnitId(), y.getFactor()) : y) // you might want to create a copy of y instead
        .collect(Collectors.toList());

ТретийАргумент в основном говорит о том, как мы хотим объединить элементы.Здесь лямбда говорит:

Если элементы двух объектов равны, то мы создаем новый элемент с идентификатором из первого элемента (из списка 1) и все остальное из второго элемента (изпесни2).В противном случае просто используйте второй элемент.

Это приведет к тому, что некоторые объекты в result будут совершенно новыми, а некоторые - уже существующими.Если вы хотите, чтобы все они были совершенно новыми, замените y в самом конце этой строки на:

new Factor(y.getID(), y.getItem(), y.getUnitId(), y.getFactor())
0 голосов
/ 29 марта 2019

при условии, что у вас также есть переопределенный hashCode метод, который вы можете использовать toMap коллектор:

Collection<Factor> values = Stream.concat(listA.stream(), listB.stream())
            .collect(Collectors.toMap(Function.identity(), 
                                      Function.identity(), (a, b) -> {
        return new Factor(a.getId(), a.getItem(), a.getUnitId(), b.getFactor());
    })).values();
...