Как перевести список A, B в ключевую карту кортежей с гуавой - PullRequest
5 голосов
/ 12 января 2012

Я прошу прощения, если этот вопрос является дубликатом, поиск был трудным, так как я не был уверен в правильном названии того, что я пытаюсь выполнить. Самое простое объяснение будет

List<A>, List<B> into Map<Key, Tuple<A,B>> where A.Key matched B.Key

Чтобы уточнить: у меня есть список объектов A и B, которые имеют общий ключ. Затем я хотел бы сопоставить эти два списка в карту, где ключ соответствует карте ключей, и кортеж A, B.

В моей голове было много идей о том, как это сделать, но большинство из них заканчиваются тем, что я чувствую, что неправильно использовал библиотеку (например, Maps.uniqueIndex и Iterables.transform). Кто-нибудь может указать мне правильное направление?

Ответы [ 3 ]

6 голосов
/ 12 января 2012

В Guava нет реализаций кортежей (пар и т. Д.). (Это еще одно обсуждение, если будет хорошей идеей вообще реализовать кортежи в Java.) Естественное отображение, которое я бы предложил, это использовать 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);

или

Multimap<Object, WithKey> m = ArrayListMultimap.create();
for (WithKey w : Iterables.concat(as, bs)) m.put(w.key(), w);

Вы должны проверить свои инварианты перед использованием мультикарты (или во время итерации по записям мультикарты), например, могут быть ключи только с экземпляром A или B. (Это не должно быть проблемой производительности, поскольку это может быть сделано лениво с помощью Iterables.filter.)

Дубликаты одного типа - это другая проблема. Вы можете проверить их или использовать HashMultimap, чтобы игнорировать их. Можно даже создать мультикарту с ограниченным набором для значений, который проверяет уникальность значения (см. Multimaps.newSetMultimap (Карта> карта, Поставщик> фабрика) и Constraints.constrainedSet (Установить набор, Ограничительное ограничение) ). Преимущество в том, что он быстро выходит из строя.

С этими реализациями A и B:

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;
    }
}

вывод:

{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}]}

Обновление:

Если вам нужно получить экземпляры кортежей, вы можете преобразовать 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);

Вывод ((a, b) - синтаксис кортежа):

{1=(A{k=1, v=a},B{k=1, v=2}), 3=(A{k=3, v=c},B{k=3, v=6})}
1 голос
/ 14 января 2012

Вы гарантировано, что ключи являются уникальными? (То есть у двух А нет одинакового ключа?)

Если так, я бы написал что-то вроде следующего:

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())));
}

Если вы не гарантированы, что aMap.keySet (). Equals (bMap.keySet ()), то вы должны изменить это соответствующим образом: проверьте, есть ли соответствующий B или нет, и т. Д.

0 голосов
/ 13 января 2012

Сортировка списков по ключам и преобразование двух списков в кортежи без особой помощи со стороны Гуавы вполне читабельно:

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));
}

Вывод ((a, b) - синтаксис кортежа):

{1=(A{k=1, v=a},B{k=1, v=2}), 3=(A{k=3, v=c},B{k=3, v=6})}

Это может быть реализовано лучше, когда Guava поддерживает zip аналогично 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)]
...