Reputation: 24557
I have a List of Objects A
, which is used to retrieve another List of Objects B
. But the second List is randomly sorted. Both Object types have the id
property in common.
What I'm doing at the moment:
List<A> input = ...;
List<B> output = new ArrayList<>();
for(A a : input) {
output.add(getOutputObjectById(a.getId()));
}
The problem is, that getOutputObjectById
is quite expensive. There's another method getOutputObjectsByIds
which takes a Collection
of ids and returns List<B>
. But the order of elements can be different. Though I need a way to ensure that both Lists have the same sorting at the end.
I first thought about using a LinkedHashMap
and do something like this:
List<A> input = ...;
List<B> output = new ArrayList<>();
LinkedHashMap<String, Object> intermediate = new LinkedHashMap<String, Object>();
for(A a : input) {
intermediate.put(a.getId(), a);
}
for(B b : getOutputObjectsByIds(intermediate.keySet())) {
intermediate.put(b.getId(), b);
}
for(Object o : intermediate.values()) {
output.add((B) o);
}
That's so much code, copying objects around multiple collections, etc.
I really hope, there's a shorter and more elegant way to do this.
Do you have any idea?
Upvotes: 2
Views: 88
Reputation: 24557
Based on hexafraction's work, I created a quite similar solution, which doesn't create a new List
, but instead sorts the given List
:
/*
* converts a Collection<T> to Map<ID, T>
* ID is specified by the keyFunction
*/
public static <T, ID> Map<ID, T> collectionToMap(Collection<T> collection, Function<T, ID> keyFunction) {
return collection.stream().collect(Collectors.toMap(keyFunction, Function.identity()));
}
/*
* sorts List<T> to have the same ordering as List<O>
* two functions have to be provided, that are used to get the List's ID
*/
public static <T, O, ID> void sortListByObjects(List<T> list, Function<T, ID> listIdFunction, List<O> objects, Function<O, ID> objectIdFunction) {
Map<ID, T> map = collectionToMap(list, listIdFunction);
for(int i = 0; i < objects.size(); i++) {
list.set(i, map.get(objectIdFunction.apply(objects.get(i))));
}
}
/*
* abstraction of the method above, which sorts List<T> by a List of IDs
*/
public static <T, ID> void sortListByIds(List<T> list, Function<T, ID> listIdFunction, List<ID> ids) {
sortListByObjects(list, listIdFunction, ids, Function.identity());
}
It's pretty simple to use:
List<String> ids = Arrays.asList("4", "1", "9");
List<Foo> unsortedList = Arrays.asList(new Foo("1"), new Foo("9"), new Foo("4"));
sortListByIds(unsortedList, foo -> foo.getId(), ids);
Or:
List<Foo> unsortedList = Arrays.asList(new Foo("1"), new Foo("9"), new Foo("4"));
List<Bar> sortedList = Arrays.asList(new Bar("4"), new Bar("1"), new Bar("4"));
sortListByObjects(unsortedList, foo -> foo.getId(), sortedList, bar -> bar.getId());
Upvotes: 0
Reputation: 41281
It looks like you tagged this java-8
so I'll offer use of streams:
List<A> input = ...;
HashMap<IdClass, B> bsById = new HashMap<>();
for(B b : getOutputObjectsByIds(input.stream().map(A::getId).collect(Collectors.toList()))){
bsById.put(b.getId(), b);
}
return input.stream().map((A a)->bsById.get(a.getId())).collect(Collectors.toList());
This uses an intermediate HashMap
, to avoid a O(n^2) situation with a parallel scan over the list for each member of input
.
Upvotes: 3