如何优雅地交换 Map 中的键和值

发布于 2024-10-07 08:28:03 字数 291 浏览 1 评论 0原文

我已经知道如何以困难的方式做到这一点并让它发挥作用 - 迭代条目并“手动”交换。但我想知道是否像许多任务一样,可以用更优雅的方式解决这个问题。

我已阅读这篇文章,不幸的是它没有优雅的特点解决方案。我也无法使用任何奇特的 Guava BiMaps 或 jdk 之外的任何东西(项目堆栈已经定义)。

我可以假设我的地图是双射的,顺便说一句:)

I already know how to do it the hard way and got it working - iterating over entries and swapping "manually". But i wonder if, like so many tasks, this one can be solved in a more elegant way.

I have read this post, unfortunately it does not feature elegant solutions. I also have no possibility to use any fancy Guava BiMaps or anything outside the jdk (project stack is already defined).

I can assume that my map is bijective, btw :)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(10

闻呓 2024-10-14 08:28:03
Map<String, Integer> map = new HashMap<>();
Map<Integer, String> swapped = map.entrySet().stream().collect(Collectors.toMap(Map.Entry::getValue, Map.Entry::getKey));
Map<String, Integer> map = new HashMap<>();
Map<Integer, String> swapped = map.entrySet().stream().collect(Collectors.toMap(Map.Entry::getValue, Map.Entry::getKey));
故事还在继续 2024-10-14 08:28:03

如果您无法选择使用第三方库,我不认为以下代码如此丑陋(尽管某些脚本语言确实有优雅的方法来做到这一点):

//map must be a bijection in order for this to work properly
public static <K,V> HashMap<V,K> reverse(Map<K,V> map) {
    HashMap<V,K> rev = new HashMap<V, K>();
    for(Map.Entry<K,V> entry : map.entrySet())
        rev.put(entry.getValue(), entry.getKey());
    return rev;
}

If you don't have a choice to use a third party library, I don't consider the following code so ugly (though some scripting languages do have elegant ways of doing it):

//map must be a bijection in order for this to work properly
public static <K,V> HashMap<V,K> reverse(Map<K,V> map) {
    HashMap<V,K> rev = new HashMap<V, K>();
    for(Map.Entry<K,V> entry : map.entrySet())
        rev.put(entry.getValue(), entry.getKey());
    return rev;
}
携君以终年 2024-10-14 08:28:03

标准 API/Java 运行时不提供双向映射,因此唯一的解决方案是迭代所有条目并手动交换它们。

您可以做的是创建一个包含两个映射的包装类,并在内部执行双重 put() 操作,以便您可以快速获得数据的两个视图。

[编辑] 另外,由于开源,您不必包含第三方库,您可以简单地将所需的类复制到您自己的项目中。

The standard API / Java runtime doesn't offer a bi-directional map, so the only solution is to iterate over all entries and swap them manually.

What you can do is create a wrapper class which contains two maps and which does a dual put() internally so you have fast two views on the data.

[EDIT] Also, thanks to open source, you don't have to include a third party library, you can simply copy the classes you need into your own project.

双马尾 2024-10-14 08:28:03

映射不像列表,可以通过交换头部和尾部来反转列表。

地图中的对象具有计算的位置,并且使用值作为键和键作为值将需要重新计算存储位置,本质上是构建另一个地图。没有优雅的方法。

然而,有双向地图。这些可能适合您的需求。我会重新考虑使用第三方库。

Maps are not like lists, which can be reversed by swapping head with tail.

Objects in maps have a computed position, and using the value as key and the key as value would requiere to re-compute the storage place, essentialy building another map. There is no elegant way.

There are, however, bidirectional maps. Those may suit your needs. I'd reconsider using third-party libraries.

微暖i 2024-10-14 08:28:03

有些工作可以简化到一定程度,但不能再简化了。这可能就是其中之一!

如果您只想使用 Java 集合 api 来完成这项工作,那么暴力是可行的方法 - 它会很快(除非集合很大),并且将是一段明显的代码。

There are some jobs that can be simplified to a certain point and no more. This may be one of them!

If you want to do the job using Java collections apis only then brute force is the way to go - it will be quick (unless the collection is huge) and it will be an obvious piece of code.

幸福不弃 2024-10-14 08:28:03

作为回答的提示
https://stackoverflow.com/a/42091477/8594421

仅当映射不是 HashMap 并且不不包含重复值。

Map<String,String> newMap = oldMap.entrySet().stream().collect(Collectors.toMap(Map.Entry::getValue, Map.Entry::getKey));

抛出异常

java.lang.IllegalStateException:重复键

java.lang.IllegalStateException:如果值多次出现,则会出现

。解决办法:

HashMap<String,String> newMap = new HashMap<>();

for(Map.Entry<String,String> entry : oldMap.entrySet())
        newMap.put(entry.getValue(), entry.getKey());

// Add inverse to old one
oldMap.putAll(newMap);

As a hint to answer
https://stackoverflow.com/a/42091477/8594421

This only works, if the map is not a HashMap and does not contain duplicate values.

Map<String,String> newMap = oldMap.entrySet().stream().collect(Collectors.toMap(Map.Entry::getValue, Map.Entry::getKey));

throws an exception

java.lang.IllegalStateException: Duplicate key

if there are values more than once.

The solution:

HashMap<String,String> newMap = new HashMap<>();

for(Map.Entry<String,String> entry : oldMap.entrySet())
        newMap.put(entry.getValue(), entry.getKey());

// Add inverse to old one
oldMap.putAll(newMap);
我三岁 2024-10-14 08:28:03

如果您有权访问 apache commons-collections,则可以使用 MapUtils.invertMap

注意:重复值的行为未定义。

(回复此,因为这是“java invert map”的第一个谷歌结果)。

If you had access to apache commons-collections, you could have used MapUtils.invertMap.

Note: The behaviour in case of duplicated values is undefined.

(Replying to this as this is the first google result for "java invert map").

弃爱 2024-10-14 08:28:03

Java 流 API 提供了一组很好的 API 可以帮助您完成此任务。

如果值是唯一的,则以下内容将起作用。当我指的是值时,我指的是 Map中的 V。

Map<String, Integer> map = new HashMap<>();
Map<Integer, String> swapped = map.entrySet()
                                  .stream()
                                  .collect(Collectors.toMap(Map.Entry::getValue, Map.Entry::getKey));

如果值不唯一,则使用以下内容:

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

感谢 Nikita 和 FreyaZ。作为新答案发布,因为 Nikita 的答案有很多编辑队列

Java stream API provides nice set of APIs that would help you with this.

If the values are unique then the below would work. When I mean values, I mean the V in the Map<K, V>.

Map<String, Integer> map = new HashMap<>();
Map<Integer, String> swapped = map.entrySet()
                                  .stream()
                                  .collect(Collectors.toMap(Map.Entry::getValue, Map.Entry::getKey));

If the values are not unique, then use below:

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

Thanks Nikita and FreyaZ. Posting as new answer as there were so many edit queues for Nikita's Answer

靖瑶 2024-10-14 08:28:03

这也适用于映射中的重复值,但不适用于 HashMap 作为值。

package Sample;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

public class Sample {
    public static void main(String[] args) { 
        Map<String,String> map = new HashMap<String,String>(); 
        Map<String, Set<String> > newmap = new HashMap<String, Set<String> >(); 

        map.put("1", "a"); 
        map.put("2", "a"); 
        map.put("3", "b"); 
        map.put("4", "b"); 
        System.out.println("before Reversing \n"+map.toString()); 

        for (Map.Entry<String, String> entry : map.entrySet()) 
        { 
            String oldVal = entry.getValue(); 
            String oldKey = entry.getKey(); 
            Set<String> newVal = null; 

            if (newmap.containsKey(oldVal)) 
            { 
                newVal = newmap.get(oldVal); 
                newVal.add(oldKey); 
            } 
            else 
            { 
                newVal= new HashSet<>(); 
                newVal.add(oldKey); 
            } 
            newmap.put(oldVal, newVal); 
        } 
        System.out.println("After Reversing \n "+newmap.toString()); 
    } 
}

This will work for duplicate values in the map also, but not for HashMap as values.

package Sample;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

public class Sample {
    public static void main(String[] args) { 
        Map<String,String> map = new HashMap<String,String>(); 
        Map<String, Set<String> > newmap = new HashMap<String, Set<String> >(); 

        map.put("1", "a"); 
        map.put("2", "a"); 
        map.put("3", "b"); 
        map.put("4", "b"); 
        System.out.println("before Reversing \n"+map.toString()); 

        for (Map.Entry<String, String> entry : map.entrySet()) 
        { 
            String oldVal = entry.getValue(); 
            String oldKey = entry.getKey(); 
            Set<String> newVal = null; 

            if (newmap.containsKey(oldVal)) 
            { 
                newVal = newmap.get(oldVal); 
                newVal.add(oldKey); 
            } 
            else 
            { 
                newVal= new HashSet<>(); 
                newVal.add(oldKey); 
            } 
            newmap.put(oldVal, newVal); 
        } 
        System.out.println("After Reversing \n "+newmap.toString()); 
    } 
}
南…巷孤猫 2024-10-14 08:28:03

无论映射中使用什么数据类型,此方法都会将键与值交换。

public static <K,V> Map<V,K> reverseOneToOneMap(Map<K,V> map) {
    return map.entrySet().stream().collect(Collectors.toMap(
            Map.Entry::getValue, Map.Entry::getKey));
}

如果并非所有值都是唯一的(因此不能成为唯一键),则会抛出 IllegalStateException。

This method will swap keys with values, regardless of the data types used in the map.

public static <K,V> Map<V,K> reverseOneToOneMap(Map<K,V> map) {
    return map.entrySet().stream().collect(Collectors.toMap(
            Map.Entry::getValue, Map.Entry::getKey));
}

IllegalStateException is thrown if not all values are unique (and therefore cannot become unique keys).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文