Creating a lambda function to iterate collections simultaneously - java

I'm trying Java 8, I want to iterate over 2 collections and call a parameter function for each pair of values.
In abstract, I want to apply a foo(tuple, i) function for each iteration
[ v1, v2, v3, v4, v5, v6 ] (first collection)
[ w1, w2, w3, w4, w5, w6 ] (second collection)
---------------------------
foo(<v1,w1>, 0)
foo(<v2,w2>, 1)
...
foo(<v6,w6>, 5)
Now what I got so far (java and pseudo code)
// Type of f?
private <S,U> void iterateSimultaneously(Collection<S> c1, Collection<U> c2, Function f) {
int i = 0
Iterator<S> it1 = c1.iterator()
Iterator<U> it2 = c2.iterator()
while(it1.hasNext() && it2.hasNext()) {
Tuple<S, U> tuple = new Tuple<>(it1.next(), it2.next())
// call somehow f(tuple, i)
i++
}
}
// ........................
// pseudo code, is this posible in Java?
iterateSimultaneously(c1, c2, (e1, e2, i) -> {
// play with those items and the i value
})

Is something like this what you're looking for?
private <S,U> void iterateSimultaneously(Collection<S> c1, Collection<U> c2, BiConsumer<Tuple<S, U>, Integer> f) {
int i = 0
Iterator<S> it1 = c1.iterator()
Iterator<U> it2 = c2.iterator()
while(it1.hasNext() && it2.hasNext()) {
Tuple<S, U> tuple = new Tuple<>(it1.next(), it2.next())
f.accept(tuple, i);
i++
}
}
iterateSimultaneously(c1, c2, (t, i) -> {
//stuff
})
What type is the function f supposed to return? If nothing, change it to a consumer instead. If you want it to accept a tuple you most clarify it like I have done here. Is this what you're looking for?

You are probably looking for a BiConsumer:
private <S,U> void iterateSimultaneously(Collection<S> c1, Collection<U> c2,
BiConsumer<Tuple<S, U>, Integer> f) {
f.accept(tuple, i);
}
and call it with:
iterateSimultaneously(c1, c2, (tuple, i) -> doSomethingWith(tuple, i));
The signature of doSomethingWith would look like:
private <S, U> void doSomethingWith(Tuple<S, U> tuple, int i) {
}

You can find an detailed implementation using Stream API of Java 8 of what you are looking for just here (the method zip()) :
https://github.com/JosePaumard/streams-utils/blob/master/src/main/java/org/paumard/streams/StreamsUtils.java#L398

Take a look at Guava's utilities for streams, particularly Streams.zip and Streams.mapWithIndex. You might use them both to achieve what you want:
Collection<Double> numbers = Arrays.asList(1.1, 2.2, 3.3, 4.4, 5.5);
Collection<String> letters = Arrays.asList("a", "b", "c", "d", "e");
Stream<Tuple<Double, String>> zipped = Streams.zip(
numbers.stream(),
letters.stream(),
Tuple::new);
Stream<String> withIndex = Streams.mapWithIndex(
zipped,
(tuple, index) -> index + ": " + tuple.u + "/" + tuple.v);
withIndex.forEach(System.out::println);
This produces the following output:
0: 1.1/a
1: 2.2/b
2: 3.3/c
3: 4.4/d
4: 5.5/e
This works by first zipping streams for c1 and c2 collections into one zipped stream of tuples and then mapping this zipped stream with a function that receives both each tuple and its corresponding index.
Note that Streams.mapWithIndex must receive a BiFunction, which means that it must return a value. If you want to consume both the tuples and the indices instead, I'm afraid you will need to create a new tuple containing the original tuple and the index:
Stream<Tuple<Tuple<Double, String>, Long>> withIndex = Streams.mapWithIndex(
zipped,
Tuple::new);
withIndex.forEach(tuple -> someMethod(tuple.u, tuple.v));
Where someMethod has the following signature:
<U, V> void method(Tuple<U, V> tuple, long index)
Note 1: this example assumes the following Tuple class is used:
public class Tuple<U, V> {
private final U u;
private final V v;
Tuple(U u, V v) {
this.u = u;
this.v = v;
}
// TODO: getters and setters, hashCode and equals
}
Note 2: while you can achieve the same with iterators, the main advantage of these utilities is that they also work efficiently on parallel streams.
Note 3: this functionality is available since Guava 21.0.

Related

How to zip two Java Lists

I have 2 Lists:
List<String> subjectArr = Arrays.asList<String>("aa", "bb", "cc");
List<Long> numArr = Arrays.asList<Long>(2L, 6L, 4L);
How do I create new List and zip two Lists into it?
List<?> subjectNumArr = zip(subjectArr, numArr);
// subjectNumArr == [{'aa',2},{'bb',6},{'cc',4}]
Here's Java-8 solution using the Pair class (like in #ZhekaKozlov answer):
public static <A, B> List<Pair<A, B>> zipJava8(List<A> as, List<B> bs) {
return IntStream.range(0, Math.min(as.size(), bs.size()))
.mapToObj(i -> new Pair<>(as.get(i), bs.get(i)))
.collect(Collectors.toList());
}
In Java 9 onwards you can use Map.entry():
public static <A, B> List<Map.Entry<A, B>> zipJava8(List<A> as, List<B> bs) {
return IntStream.range(0, Math.min(as.size(), bs.size()))
.mapToObj(i -> Map.entry(as.get(i), bs.get(i)))
.collect(Collectors.toList());
}
As per related question, you can use Guava (>= 21.0) to do this:
List<String> subjectArr = Arrays.asList("aa", "bb", "cc");
List<Long> numArr = Arrays.asList(2L, 6L, 4L);
List<Pair> pairs = Streams.zip(subjectArr.stream(), numArr.stream(), Pair::new)
.collect(Collectors.toList());
Note that the guava method is annotated as #Beta, though what that means in practice is up to interpretation, the method has not changed since version 21.0.
To get an Iterator<C> from an Iterator<A>, an Iterator<B>, and a BiFunction<A, B, C>:
public static <A, B, C> Iterator<C> map(Iterator<A> a, Iterator<B> b, BiFunction<A, B, C> f) {
return new Iterator<C>() {
public boolean hasNext() {
return a.hasNext() && b.hasNext(); // This uses the shorter of the two `Iterator`s.
}
public C next() {
return f.apply(a.next(), b.next());
}
};
}
Use an ArrayList of Map.Entry<String, Long>, checking that both arraylists have equal size (as it seems to be your requirement), like that:
List<Map.Entry<String,Long>> subjectNumArr = new ArrayList<>(numArr.size());
if (subjectArr.size() == numArr.size()) {
for (int i = 0; i < subjectArr.size(); ++i) {
subjectNumArr.add(new AbstractMap.SimpleEntry<String, Long>(subjectArr.get(i), numArr.get(i));
}
}
That's all the code you need!
Then, to iterate over the results, use something like:
for (Map.Entry<String, Long> entry : subjectNumArr) {
String key = entry.getKey();
Long value = entry.getValue();
}
or, you can simply get the pair at position i (keeping insertion order), by:
Map.Entry<String, Long> entry = subjectNumArr.get(i);
This can also hold duplicate entries, unlike the Map solution that I initially suggested, without requiring to define your own (Pair) class.
The operation you want is called zipping.
You need to implement a method zip:
public static <A, B> List<Pair<A, B>> zip(List<A> as, List<B> bs) {
Iterator<A> it1 = as.iterator();
Iterator<B> it2 = bs.iterator();
List<Map.Entry<A, B>> result = new ArrayList<>();
while (it1.hasNext() && it2.hasNext()) {
result.add(Map.entry(it1.next(), it2.next()));
}
return result;
}
And you use it like this:
zip(subjectArr, numArr);
I agree with vefthym however if you have to do using list then create a class like below -:
class DirtyCoding{
String subject;
int numbr;
}
Then iterate over the your list, create object of DirtyCoding, populate it and add then add it to List<DirtyCoding>.
My ideas:
Define a class for your pairs. This makes your code extendable (i.e. if you want to add a third field).
Define your Lists with the convinient method Arrays.asList. It is easy to understand, short and automatically generates generic collections.
Use superclasses or interfaces as variable types. I used List in the example, maybe Collection would be even better. Only declare variables as ArrayList if you need the list to be so specific. That will give you the possibility to use other implementations, without having to change much code.
I would create Pair objects like this:
import java.util.*;
class Pair {
String subject;
Long num;
}
public class Snippet {
public static void main(String[] args) {
List<String> subjectArr = Arrays.asList("aa", "bb", "cc");
List<Long> numArr = Arrays.asList(2l,6l,4l);
// create result list
List<Pair> pairs = new ArrayList<>();
// determine result size
int length = Math.min(subjectArr.size(), numArr.size());
// create pairs
for (int position = 0; position < length; position++) {
Pair pair = new Pair();
pair.subject = subjectArr.get(position);
pair.num = numArr.get(position);
pairs.add(pair);
}
}
}
Use one of the answers from Zipping streams using JDK8 with lambda (java.util.stream.Streams.zip)
to zip and apply a function at the same time
e.g. Using a zipped Stream:
<A,B,C> Stream<C> zipped(List<A> lista, List<B> listb, BiFunction<A,B,C> zipper){
int shortestLength = Math.min(lista.size(),listb.size());
return IntStream.range(0,shortestLength).mapToObject( i -> {
return zipper.apply(lista.get(i), listb.get(i));
});
}
for which you may also use Guava's Streams.zip()
You should create an ArrayList of List:
ArrayList<List> subjectNumArr = new ArrayList<>();
Iterator iter = subjectArr.iterator();
int count=0;
while(iter.hasNext()){
subjectNumArr.add(Arrays.asList(iter.next(),numArr.get[count++]);
}
In Java 8:
You can do this in one line using Stream and Collectors class.
In Java 7/6/5:
List list = new ArrayList();
if(subjectArr.size() == numArr.size())
{
for (int i = 0; i < subjectArr.size(); i++) { // Loop through every subject/name
list.add(subjectArr.get(i) + " " + numArr.get(i)); // Concat the two, and add it
}
}

java 8 functional style of chaining links

I have a Map<String , String> which indicates links from A to B. I want to chain all possible routes. for example :
[A , B]
[B , C]
[C , D]
[E , F]
[F , G]
[H , I]
will output
[A , B , C , D]
[E , F , G]
[H , I]
I found similar question here (but not fully fulfills my requirement) : https://stackoverflow.com/a/10176274/298430
And here is my solution :
public static <T> Set<List<T>> chainLinks(Map<T , T> map) {
Set<List<T>> resultSet = new HashSet<>();
map.forEach((from, to) -> {
if (!map.containsValue(from)) {
List<T> list = new ArrayList<>();
list.add(from);
list.addAll(inner(to, map));
resultSet.add(list);
}
});
return resultSet;
}
private static <T> List<T> inner(T from , Map<T , T> map) {
if (map.containsKey(from)) {
List<T> list = new ArrayList<>();
list.add(from);
list.addAll(inner(map.get(from), map));
return list;
} else {
List<T> end = new ArrayList<>();
end.add(from);
return end;
}
}
and the test case :
#Test
public void testChainLinks() {
Map<String , String> map = new HashMap<String , String>() {{
put("A" , "B");
put("B" , "C");
put("C" , "D");
put("E" , "F");
put("F" , "G");
put("H" , "I");
}};
Utils.chainLinks(map).forEach(list -> {
logger.info("list = {}" , list.stream().collect(Collectors.joining(" -> ")));
});
}
It does work correctly :
list = H -> I
list = E -> F -> G
list = A -> B -> C -> D
But I don't like my solution. Because I feel it can be solved in a more functional-style . I can feel the smell of stream.fold() here . I tried but in vain to convert my code to a pure functional style : which means no intermediate objects creation...
Is it possible ? Any hints are grateful !
Non-recursive solution:
Set<List<String>> result = map.keySet().stream()
.filter(k -> !map.containsValue(k))
.map(e -> new ArrayList<String>() {{
String x = e;
add(x);
while (map.containsKey(x))
add(x = map.get(x));
}})
.collect(Collectors.toSet());
EDIT: included filter from David PĂ©rez Cabrera's comment to remove intermediate lists.
Well you can easily recursion:
private static Set<List<String>> chainLinks(Map<String, String> map) {
return map.keySet().stream().filter(k -> !map.containsValue(k)).map( (key) ->
calc(key, map, new LinkedList<>())
).collect(Collectors.toSet());
}
private static List<String> calc(String key,Map<String, String> map,List<String> list){
list.add(key);
if (map.containsKey(key))
return calc(map.get(key),map,list);
else
return list;
}
There's an alternative solution using the custom collector with close to linear complexity. It's really faster than the solutions proposed before, though looks somewhat uglier.
public static <T> Collector<Entry<T, T>, ?, List<List<T>>> chaining() {
BiConsumer<Map<T, ArrayDeque<T>>, Entry<T, T>> accumulator = (
m, entry) -> {
ArrayDeque<T> k = m.remove(entry.getKey());
ArrayDeque<T> v = m.remove(entry.getValue());
if (k == null && v == null) {
// new pair does not connect to existing chains
// create a new chain with two elements
k = new ArrayDeque<>();
k.addLast(entry.getKey());
k.addLast(entry.getValue());
m.put(entry.getKey(), k);
m.put(entry.getValue(), k);
} else if (k == null) {
// new pair prepends an existing chain
v.addFirst(entry.getKey());
m.put(entry.getKey(), v);
} else if (v == null) {
// new pair appends an existing chain
k.addLast(entry.getValue());
m.put(entry.getValue(), k);
} else {
// new pair connects two existing chains together
// reuse the first chain and update the tail marker
// btw if k == v here, then we found a cycle
k.addAll(v);
m.put(k.getLast(), k);
}
};
BinaryOperator<Map<T, ArrayDeque<T>>> combiner = (m1, m2) -> {
throw new UnsupportedOperationException();
};
// our map contains every chain twice: mapped to head and to tail
// so in finisher we have to leave only half of them
// (for example ones connected to the head).
// The map step can be simplified to Entry::getValue if you fine with
// List<Collection<T>> result.
Function<Map<T, ArrayDeque<T>>, List<List<T>>> finisher = m -> m
.entrySet().stream()
.filter(e -> e.getValue().getFirst().equals(e.getKey()))
.map(e -> new ArrayList<>(e.getValue()))
.collect(Collectors.toList());
return Collector.of(HashMap::new, accumulator, combiner, finisher);
}
Usage:
List<List<String>> res = map.entrySet().stream().collect(chaining());
(I did not implement the combiner step, thus it cannot be used for parallel streams, but it's not very hard to add it as well). The idea is simple: we track partial chains found so far in the map where keys point to chain starts and ends and the values are ArrayDeque objects containing the chains found so far. Every new entry updates existing deque (if it appends/prepends it) or merges two deques together.
According to my tests this version works 1000x faster than #saka1029 solution for the 50000 element input array with 100 chains.

Efficient sort-by function

For a Java project I'm working on, I need to make a sort-by method, which sorts a list using a mapping function. The most obvious solution is to use the built-in Collections.sort() method:
static <D, R extends Comparable> void sortBy(List<D> list, Function<D, R> function) {
Collections.sort(list, new Comparator<D>() {
#Override
public int compare(D d1, D d2) {
return function.apply(d1).compareTo(function.apply(d2));
}
});
}
The problem is that this calls the function on each element many times (I think about 2 log N). Furthermore, the function is likely to be slow, with each invocation taking at least a few milliseconds, possibly much longer. I'd like a more efficient algorithm which calls the function as few times as possible.
I've considered applying each function at the beginning, and then sorting the mapped list, but I don't see how to get back to the original list:
static <D, R extends Comparable> void sortBy(List<D> list, Function<D, R> function) {
List<R> newList = new ArrayList<>();
for (D d : list){
newList.add(function.apply(d));
}
Collections.sort(newList);
// now what?
}
(Note that the function is pure, i.e. each input yields the same output, with no side effects.)
As proposed in comments, you can sort a list of some kind of tuple that will hold the original value and the computed one. Then you build a new list by extracting the original values in sorted order.
This solution creates temporary objects (the tuples), but should be efficient if the mapping function is expensive. Of course, this needs to be measured...
static <D, R extends Comparable> List<D> sortBy(List<D> list, Function<D, R> function) {
// Build the list of pairs
List<Pair<D,R>> newList = list.stream()
.map(d -> new Pair<>(d, function.apply(d)))
.collect(Collectors.toList());
// Sort the list of pairs on second member, which is the computed one
Collections.sort(newList, new Comparator<Pair<D,R>>() {
#Override
public int compare(Pair<D, R> p1, Pair<D, R> p2) {
return p1.second.compareTo(p2.second);
}
});
// extract the first member of pair, which is the original value
return newList.stream().map(p -> p.first).collect(Collectors.toList());
}
Given a simple class Pair<U, V> like:
public final class Pair<U,V> {
public final U first;
public final V second;
public Pair(U u, V v) {
this.first = u;
this.second = v;
}
public String toString() {
return "["+first+","+second+"]";
}
}
Then:
List<String> data = Arrays.asList("blah", "foo", "bar", "hello world", "bye bye", "fizz", "buzz");
List<String> sortedDataByLength = sortBy(data, new Function<String, Integer>() {
#Override
public Integer apply(String t) {
return t.length();
}});
System.out.println(sortedDataByLength);
Yields:
[foo, bar, blah, fizz, buzz, bye bye, hello world]
Instead of R simply containing the result, implement it so that it is a composite object that contains a reference to its corresponding D as well. Then you can sort by R and extract D out of each R element in the sorted list.
You can implement simple memoization using the new Java 8 Map#computeIfAbsent(...) method:
static <D, R extends Comparable<? super R>> void sortBy(List<D> list, Function<D, R> function) {
Map<D, R> memo = new HashMap<>();
Collections.sort(list, new Comparator<D>() {
#Override
public int compare(D d1, D d2) {
R r1 = memo.computeIfAbsent(d1, function);
R r2 = memo.computeIfAbsent(d2, function);
return r1.compareTo(r2);
}
});
}

Combine two streams with collation

I need to do some Matrix work in an efficient and flexible way and hoped I could practice my Java 8 using streams and lambdas and perhaps even get free parallelism out of it. One point I am struggling with is how to perform an operation on two streams putting the result into a third.
Consider the simple mechanism:
static final List<String> a = Arrays.asList("A", "A", "A");
static final List<String> b = Arrays.asList("B", "B", "B");
public void withoutStreams() {
// The boring old way.
List<String> c = new ArrayList<>();
for (Iterator<String> ai = a.iterator(), bi = b.iterator(); ai.hasNext() && bi.hasNext();) {
c.add(ai.next() + bi.next());
}
System.out.println(c);
}
Works fine but I want to use Streams.
private void withStreams() {
List<String> c = new ArrayList<>();
combine(a.stream(), b.stream(), c, (String x, String y) -> x + y);
}
private void combine(Stream<String> a, Stream<String> b, List<String> c, BinaryOperator<String> op) {
// What can I do here to make it happen?
}
I fully expect we will be filling c using a Consumer of some form but extra kudos for coming up with some way of referring to a specific cell of the matrix other than using (row,col) bearing in mind that the cells will be immutable.
You can use the IntStream class to simulate indexing and then .mapToObj to concatenate the corresponding to the index objects from a and b:
List<String> list = IntStream.range(0, Math.max(a.size(), b.size()))
.mapToObj(i -> a.get(i) + b.get(i))
.collect(Collectors.toList());
Applied to your method, this will look like:
private void combine(List<String> a, List<String> b, List<String> c, BinaryOperator<String> op) {
c = IntStream.range(0, Math.max(a.size(), b.size()))
.mapToObj(i -> op.apply(a.get(i), b.get(i)))
.collect(Collectors.toList());
}
However, if you don't want to change the method's signature, here is a solution which works for all possible combinations of infinite and finite streams:
private void combine(Stream<String> a, Stream<String> b, List<String> c, BinaryOperator<String> op) {
Iterator<String> i1 = a.iterator();
Iterator<String> i2 = b.iterator();
Iterable<String> i = () -> new Iterator<String>() {
public boolean hasNext() {
return i1.hasNext() && i2.hasNext();
}
public String next() {
return op.apply(i1.next(), i2.next());
}
};
c = StreamSupport.stream(i.spliterator(), false).collect(Collectors.toList());
}
Functional programming style, using recursive (no loop):
static Stream<String> combine(List<String> a, List<String> b) {
if(a.isEmpty() || b.isEmpty()) {
return Stream.empty();
}
return Stream.concat(
Stream.of(a.get(0) + b.get(0)),
combine(a.stream().skip(1).collect(Collectors.toList()),
b.stream().skip(1).collect(Collectors.toList()))
);
}
plus: I vote up kocko's answer, my answer is for fun.

How to translate list A,B to keyed map of tuples with guava

I apologize if this question is a duplicate, searching was difficult as I was unsure of the proper name for what I'm trying to accomplish. The simplest explanation would be
List<A>, List<B> into Map<Key, Tuple<A,B>> where A.Key matched B.Key
To clarify: I have a list of A object and B object that share a key. I'd like to then correlate these two lists into a map where the key matches into a map of key, and tuple A,B.
I've played around with many ideas on how to do this in my head, but most of them end with me feeling like I've misused the library (such as Maps.uniqueIndex, and Iterables.transform). Can anyone point me in the right direction?
There are no tuple (pair etc.) implementations in Guava. (It's another discussion if it's good idea to implementation tuples in Java at all.) The natural mapping I would suggest is to use a Multimap:
List<A> as = Lists.newArrayList(new A(1, "a"), new A(3, "c"), new A(2, "b"));
List<B> bs = Lists.newArrayList(new B(1, 2), new B(3, 6), new B(5, 10));
Function<WithKey, Object> toKey = new Function<WithKey, Object>() {
#Override public Object apply(WithKey input) { return input.key(); }
};
ImmutableListMultimap<Object, AbstractWithKey> index =
Multimaps.index(Iterables.concat(as, bs), toKey);
or
Multimap<Object, WithKey> m = ArrayListMultimap.create();
for (WithKey w : Iterables.concat(as, bs)) m.put(w.key(), w);
You have to check your invariants before using the multimap (or while your iterating over the multimap entries) for example there could be keys with only a A or B instance. (This shouldn't be a performance issue as it can be done lazily with Iterables.filter.)
Duplicates of one type is another issue. You could check them or use a HashMultimap to ignore them. You could even build a multimap with a constrainted set for values that checks that a value is unique (see Multimaps.newSetMultimap(Map> map, Supplier> factory) and Constraints.constrainedSet(Set set, Constraint constraint)). This has the advantage that it fails fast.
With these A and B implementations:
interface WithKey {
Object key();
}
abstract class AbstractWithKey implements WithKey {
Object key;
Object v;
#Override public Object key() { return key; }
#Override public String toString() {
return MoreObjects.toStringHelper(this).add("k", key).add("v", v).toString();
}
}
class A extends AbstractWithKey {
public A(int i, String v) {
key = i;
this.v = v;
}
}
class B extends AbstractWithKey {
public B(int i, int v) {
key = i;
this.v = v;
}
}
the output is:
{1=[A{k=1, v=a}, B{k=1, v=2}], 2=[A{k=2, v=b}], 3=[A{k=3, v=c}, B{k=3,
v=6}], 5=[B{k=5, v=10}]}
Update:
If you have to end up with your tuple instances, you can transform the Multimap.
Multimap<Object, WithKey> m = ArrayListMultimap.create();
for (WithKey w : Iterables.concat(as, bs)) m.put(w.key(), w);
Function<Collection<WithKey>, Tuple> f =
new Function<Collection<WithKey>, Tuple>(){
#Override public Tuple apply(Collection<WithKey> input) {
Iterator<WithKey> iterator = input.iterator();
return new Tuple(iterator.next(), iterator.next());
} };
Map<Object, Tuple> result = Maps.transformValues(m.asMap(), f);
Output ((a,b) is the tuple syntax):
{1=(A{k=1, v=a},B{k=1, v=2}), 3=(A{k=3, v=c},B{k=3, v=6})}
Are you guaranteed that keys are unique? (That is, that no two A's have the same key?)
If so, I'd write something like the following:
Map<Key, A> aMap = Maps.uniqueIndex(theAs, aKeyFunction); // Guava!
Map<Key, B> bMap = Maps.uniqueIndex(theBs, bKeyFunction);
Map<Key, AWithMatchingB> joinedMap = Maps.newHashMap();
for(Map.Entry<Key, A> aEntry : aMap.entrySet()) {
joinedMap.put(aEntry.getKey(), AWithMatchingB.match(
aEntry.getValue(), bMap.get(aEntry.getKey())));
}
If you're not guaranteed that aMap.keySet().equals(bMap.keySet()), then you'd modify this appropriately: check whether or not there's a matching B or not, etc.
Sorting the lists by key and transforming the two lists to tuples without much help from Guava is quite readable:
Comparator<WithKey>c = new Comparator<WithKey>(){
#Override public int compare(WithKey o1, WithKey o2) {
return o1.key().compareTo(o2.key());
}
};
Collections.sort(as, c);
Collections.sort(bs, c);
Preconditions.checkArgument(as.size() == bs.size());
Iterator<A> aIt = as.iterator();
Iterator<B> bIt = bs.iterator();
Map<Integer, Tuple> ts = Maps.newHashMap();
while(aIt.hasNext()) {
A a = aIt.next();
B b = bIt.next();
Preconditions.checkArgument(a.key().equals(b.key()));
ts.put(a.key(), new Tuple(a, b));
}
Output ((a,b) is the tuple syntax):
{1=(A{k=1, v=a},B{k=1, v=2}), 3=(A{k=3, v=c},B{k=3, v=6})}
This can be implemented nicer when Guava supports zip similar to Python:
sa = [(1, "a"), (3, "c")]
sb = [(1, 2), (3, 6)]
sa.sort()
sb.sort()
vs = [(a[0], (a,b)) for (a, b) in zip(sa, sb)]

Categories

Resources