将两个列表合并到一个映射中的最佳方法是什么(Java)?

2022-08-31 11:49:48

使用 会很好,但它只会循环访问一个列表,并且您需要另一个列表的显式迭代器。或者,您可以对两者都使用显式迭代器。for (String item: list)

下面是该问题的示例,以及使用索引循环的解决方案:for

import java.util.*;
public class ListsToMap {
  static public void main(String[] args) {
    List<String> names = Arrays.asList("apple,orange,pear".split(","));
    List<String> things = Arrays.asList("123,456,789".split(","));
    Map<String,String> map = new LinkedHashMap<String,String>();  // ordered

    for (int i=0; i<names.size(); i++) {
      map.put(names.get(i), things.get(i));    // is there a clearer way?
    }

    System.out.println(map);
  }
}

输出:

{apple=123, orange=456, pear=789}

有没有更清晰的方法?也许在集合API的某个地方?


答案 1

自从这个问题被问到以来已经有一段时间了,但现在我偏向于这样的东西:

public static <K, V> Map<K, V> zipToMap(List<K> keys, List<V> values) {
    return IntStream.range(0, keys.size()).boxed()
            .collect(Collectors.toMap(keys::get, values::get));
}

对于那些不熟悉流的人来说,这样做是获取从0到长度的一个,然后将其装箱,使其成为一个,以便它可以转换为一个对象,然后使用它们来收集它们,这需要两个供应商,其中一个生成键,另一个生成值。IntStreamStream<Integer>Collectors.toMap

这可以经得起一些验证(比如要求小于),但它作为一个简单的解决方案效果很好。keys.size()values.size()

编辑:以上适用于任何具有恒定时间查找的内容,但是如果您想要以相同顺序工作的东西(并且仍然使用相同的模式),您可以执行如下操作:

public static <K, V> Map<K, V> zipToMap(List<K> keys, List<V> values) {
    Iterator<K> keyIter = keys.iterator();
    Iterator<V> valIter = values.iterator();
    return IntStream.range(0, keys.size()).boxed()
            .collect(Collectors.toMap(_i -> keyIter.next(), _i -> valIter.next()));
}

输出是相同的(再次,缺少长度检查等),但时间复杂度不依赖于所使用的任何列表的方法的实现。get


答案 2

我经常使用以下成语。我承认,它是否更清楚是值得商榷的。

Iterator<String> i1 = names.iterator();
Iterator<String> i2 = things.iterator();
while (i1.hasNext() && i2.hasNext()) {
    map.put(i1.next(), i2.next());
}
if (i1.hasNext() || i2.hasNext()) complainAboutSizes();

它的优点是它也适用于集合和类似的东西,没有随机访问或没有有效的随机访问,如LinkedList,TreeSets或SQL ResultSets。例如,如果你在LinkedLists上使用原始算法,你就会得到一个缓慢的Shlemiel画家算法,它实际上需要对长度为n的列表进行n * n操作。

正如13ren所指出的,如果你试图在一个列表的末尾读取长度不匹配时,你也可以使用Iterator.next抛出一个NoSuchElementException的事实。因此,您将获得更简洁但可能有点令人困惑的变体:

Iterator<String> i1 = names.iterator();
Iterator<String> i2 = things.iterator();
while (i1.hasNext() || i2.hasNext()) map.put(i1.next(), i2.next());

推荐