Reputation: 159
Hope you can help me. I'll try to make myself clear about the title of the question. I have one POJO like this
public class Factor {
private int id;
private String item;
private int unitId;
private Double factor;
// constructor getters and setters
}
I have two List
of this class with data from two different tables in two different DB.
The equals
method is defined this way
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());
}
so two objects are considered equals when they have the same item
and unitId
attributes.
Let's say I have these two list:
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));
Taking the equals
method into account, the first two elements of the lists should be considered equals.
The reason why the ids
in the B list are all 0
, is because that table has not an id
field.
What I'm trying to achieve is to create a new list by merging these two lists, but if the objects are equals, it should take the id
from listA
and the factor from listB
.
In this way, the result list would have the following objects
(1, "Item1", 1, 0.8)
(2, "Item1", 2, 0.9)
(0, "Item4", 1, 1.0)
(0, "Item5", 1, 2.0)
I know how to compare both list with stream
and filter which objects are equals or different but does anybody know how to get a new object combining the equals?
Upvotes: 3
Views: 2138
Reputation: 270995
You are looking for a "zip" function. However, there isn't one in the Stream API (at least in Java 8, I don't know about newer versions).
You can find how to write a zip
method yourself in this post.
Using the answer by siki, we get this method:
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);
}
We can use it like this:
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());
The third argument is basically saying how we want to merge the items. Here the lambda says:
If the items of the two objects are equal, then we create a new item with the ID from the first item (from list1) and everything else from the second item (from list2). Otherwise, just use the second item.
This will result in some of the objects in result
to be brand new ones, and some be existing ones. If you want them to all be brand new ones, replace y
at the very end of that line with:
new Factor(y.getID(), y.getItem(), y.getUnitId(), y.getFactor())
Upvotes: 1
Reputation: 3134
assuming that you also have overridden hashCode
method you can use toMap
collector:
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();
Upvotes: 1