覆盖位于ArrayList <string>中的HashMap中的值

时间:2017-07-10 14:40:28

标签: java arraylist hashmap

假设我有一个带字符串键和整数值的HashMap:

map = {cat=1, kid=3, girl=3, adult=2, human=5, dog=2, boy=2}

我想通过将此信息放入另一个HashMap来切换键和值。我知道HashMap不能有重复的键,因此我尝试将信息放入带有Integer的HashMap中,用于映射到String ArrayList的键,以便我可以将一个Integer映射到多个字符串:

swap = {1=[cat], 2=[adult, dog, boy], 3=[kid, girl], 5=[human]}

我尝试了以下代码:

HashMap<Integer, ArrayList<String>> swap = new HashMap<Integer, ArrayList<String>>();

for (String x : map.keySet()) {
        for (int i = 0; i <= 5; i++) {
            ArrayList<String> list = new ArrayList<String>();
            if (i == map.get(x)) {
                list.add(x);
                swap.put(i, list);
            }
        }
    }

我的代码唯一的区别是我没有将数字5硬编码到我的索引中;我有一个方法,在原始的HashMap中找到最高的整数值并使用它。我知道它工作正常,因为我得到相同的输出,即使我在那里硬编码5,我只是没有包括它来节省空间。

我的目标是能够使用任何数据集进行“反转”,否则我只能对值进行硬编码。我从上面的代码得到的输出是:

swap = {1=[cat], 2=[boy], 3=[girl], 5=[human]}

正如您所看到的,我的问题是值ArrayList只保留放入其中的最后一个String,而不是收集所有String。如何使ArrayList存储每个String,而不仅仅是最后一个String?

8 个答案:

答案 0 :(得分:22)

使用Java 8,您可以执行以下操作:

Map<String, Integer> map = new HashMap<>();

map.put("cat", 1);
map.put("kid", 3);
map.put("girl", 3);
map.put("adult", 2);
map.put("human", 5);
map.put("dog", 2);
map.put("boy", 2);

Map<Integer, List<String>> newMap = map.keySet()
                                       .stream()
                                       .collect(Collectors.groupingBy(map::get));

System.out.println(newMap);

输出将是:

{1=[cat], 2=[adult, dog, boy], 3=[kid, girl], 5=[human]}

答案 1 :(得分:6)

你正在为每次迭代重新创建arrayList,我无法找到使用该逻辑的方法,这是一个很好的方法,但不需要检查最大整数:

for (Map.Entry<String, Integer> entry : map.entrySet()) {
    String key = entry.getKey();
    Integer value = entry.getValue();
    List<String> get = swap.get(value);
    if (get == null) {
        get = new ArrayList<>();
        swap.put(value, get);
    }
    get.add(key);
}

答案 2 :(得分:3)

最好的方法是迭代原始地图的键集

此外,您必须确保列表存在于目标地图中的任何键:

for (Map.Entry<String,Integer>  inputEntry : map.entrySet())
  swap.computeIfAbsent(inputEntry.getValue(),()->new ArrayList<>()).add(inputEntry.getKey());

答案 3 :(得分:2)

这显然不是最好的解决方案,但是通过交换内部和外部循环来解决问题的方法与下面所示相同。

    Map<String, Integer> map = new HashMap<String, Integer>();
    map.put("cat", 1);
    map.put("kid", 3);
    map.put("girl", 3);
    map.put("adult", 2);
    map.put("human", 5);
    map.put("dog", 2);
    map.put("boy", 2);

    HashMap<Integer, ArrayList<String>> swap = new HashMap<Integer, ArrayList<String>>();

    for (Integer value = 0; value <= 5; value++) {
        ArrayList<String> list = new ArrayList<String>();
        for (String key : map.keySet()) {
            if (map.get(key) == value) {
                list.add(key);
            }
        }
        if (map.containsValue(value)) {
            swap.put(value, list);
        }
    }

<强>输出

  

{1 = [cat],2 = [成人,狗,男孩],3 = [孩子,女孩],5 = [人类]}

答案 4 :(得分:2)

我能想到的最好方法是在现有地图上使用Map.forEach方法,在新地图上使用Map.computeIfAbsent方法:

Map<Integer, List<String>> swap = new HashMap<>();
map.forEach((k, v) -> swap.computeIfAbsent(v, k -> new ArrayList<>()).add(k));

作为旁注,您可以使用菱形运算符<>来创建新映射(在调用映射的构造函数时不需要重复键和值的类型,因为编译器会推断它们)

作为第二方,最好使用接口类型而不是具体类型,包括通用参数类型和实际类型。这就是为什么我分别使用ListMap代替ArrayListHashMap

答案 5 :(得分:1)

groupingBy中使用Jacob's answer,但Map.entrySet使用suggested by Boris以获得更好的效果,Collectors

// import static java.util.stream.Collectors.*

Map<Integer, List<String>> swap = map.entrySet()
    .stream()
    .collect(groupingBy(Entry::getValue, mapping(Entry::getKey, toList())));

这又使用了mapping的另外两种方法:toListtoMap

如果不是这两个辅助函数,解决方案可能如下所示:

Map<Integer, List<String>> swap = map.entrySet()
    .stream()
    .collect(
        groupingBy(
            Entry::getValue,
            Collector.of(
                ArrayList::new,
                (list, e) -> {
                    list.add(e.getKey());
                },
                (left, right) -> { // only needed for parallel streams
                    left.addAll(right);
                    return left;
                }
            )
        )
    );

或者,使用extension method代替groupingBy

Map<Integer, List<String>> swap = map.entrySet()
    .stream()
    .collect(
        toMap(
            Entry::getValue,
            (e) -> new ArrayList<>(Arrays.asList(e.getKey())),
            (left, right) -> {
                left.addAll(right);
                return left;
            }
        )
    );

答案 6 :(得分:0)

它接缝你覆盖将它们添加到已经褶皱的arraylist的值instrad。试试这个:

HashMap<Integer, ArrayList<String>> swapedMap = new HashMap<Integer, ArrayList<String>>();
for (String key : map.keySet()) {
    Integer swappedKey = map.get(key);

    ArrayList<String> a = swapedMap.get(swappedKey);
    if (a == null) {
        a = new ArrayList<String>();
        swapedMap.put(swappedKey, a)
    }

    a.add(key);
}

我没时间运行它(抱歉,现在没有Java编译器),但几乎可以接受:)

答案 7 :(得分:0)

您可以在merge的java-8中使用新的Map方法:

   Map<Integer, List<String>> newMap = new HashMap<>();

    map.forEach((key, value) -> {
        List<String> values = new ArrayList<>();
        values.add(key);
        newMap.merge(value, values, (left, right) -> {
            left.addAll(right);
            return left;
        });
    });