简体   繁体   English

如何在 Java 中拥有多地图功能?

[英]How to have multimap functionality in Java?

I want to have the functionality of MultiMap in Java, providing the same functionality as that of cpp MultiMap so that I am able to have multiple keys with the same value.我想在 Java 中拥有MultiMap的功能,提供与 cpp MultiMap 相同的功能,以便我能够拥有多个具有相同值的键。 Multiple elements in the container can have equivalent keys.容器中的多个元素可以有等效的键。 I thought this will work :我认为这会起作用:

TreeMap<Key, TreeMap<Key, Value> >. 

Any help is appreciated.任何帮助表示赞赏。

If you want consistent semantics, you would have to roll your own.如果你想要一致的语义,你将不得不自己动手。 The easiest way to implement it would be to back it by a Map<K, List<V>> .实现它的最简单方法是通过Map<K, List<V>>支持它。 This way you can map one key to multiple values.这样,您可以将一个键映射到多个值。

However, there are some things you need to consider regarding semantics.但是,关于语义,您需要考虑一些事项。 For example, assume you have the following multimap:例如,假设您有以下多张地图:

a -> [1, 2, 3]
b -> [4, 5]

The size of the above will be reported as 2, but could be interpreted as 5 also, if you consider that the Map can be represented as so:上面的大小将报告为 2,但也可以解释为 5,如果您认为 Map 可以表示为:

a -> 1
a -> 2
a -> 3
b -> 4
b -> 5

This has implications for the values that you return as well.这对您返回的值也有影响。 It would make more sense to return [1, 2, 3, 4, 5] and not [[1, 2, 3], [4, 5]] .返回[1, 2, 3, 4, 5]而不是[[1, 2, 3], [4, 5]]会更有意义。 This would also apply to the entry set;这也适用于条目集; you may want to return pairs as displayed above.您可能希望返回如上所示的对。

Hence a possible implementation would be to implement Map<K, V> and use a backing Map<K, List<V>> .因此,一种可能的实现是实现Map<K, V>并使用支持Map<K, List<V>> You would then have to implement the various methods while adhering to the multimap semantic.然后,您必须在遵守多图语义的同时实现各种方法。

If you don't care about the semantics but just want an ability to map a single key to multiple values, you can just use a Map<K, List<V>> directly and still get what you need.如果您不关心语义,而只是希望能够将单个键映射到多个值,则可以直接使用Map<K, List<V>>并且仍然可以获得所需的内容。

I think in this case guava BiMap is the best option.我认为在这种情况下番石榴 BiMap是最好的选择。 Since you don't want to use it then you may create own collection - TwoWayHashMap something like this:既然你不想使用它,那么你可以创建自己的集合 - TwoWayHashMap是这样的:

public class TwoWayHashmap<K extends Object, V extends Object> {

  private Map<K,V> forward = new Hashtable<K, V>();
  private Map<V,K> backward = new Hashtable<V, K>();

  public synchronized void add(K key, V value) {
    forward.put(key, value);
    backward.put(value, key);
  }

  public synchronized V getForward(K key) {
    return forward.get(key);
  }

  public synchronized K getBackward(V key) {
    return backward.get(key);
  }
}

Visit the attached link for more details.访问所附链接了解更多详情。

The Answer by Vivin Paliath is correct. Vivin Paliath 的答案是正确的。 I'll add more information, and more code.我将添加更多信息和更多代码。

Map#computeIfAbsent

To pair multiple values with one key, use collection as the value type.要将多个值与一个键配对,请使用集合作为值类型。 Often the collection is a List or Set .集合通常是ListSet Or perhaps a Map for more complicated structures.或者也许是更复杂结构的Map

As an example, suppose we want to track which employees are going to be working on which days of the week.例如,假设我们想要跟踪哪些员工将在一周中的哪几天工作。

We define Employee as a record .我们将Employee定义为记录

record Employee( int empNum , String name ) { }

Make some sample data.制作一些样本数据。

Employee alice = new Employee( 1 , "Alice" );
Employee bob = new Employee( 2 , "Bob" );
Employee carol = new Employee( 3 , "Carol" );

Each employee maps to a set of day-of-week values.每个员工都映射到一组星期几的值。 For day-of-week, we can use enum objects defined on java.time.DayOfWeek .对于星期几,我们可以使用java.time.DayOfWeek上定义的枚举对象。 So we need a Map < Employee, Set < DayOfWeek > > .所以我们需要一个Map < Employee, Set < DayOfWeek > >

Map < Employee, Set < DayOfWeek > > workDaysForEachEmployee = new HashMap <>();

Suppose we want to assign Monday to Alice.假设我们想将星期一分配给 Alice。

In earlier Java在早期的 Java 中

Here is the old way to make that assignment.这是完成该任务的旧方法。 We try to retrieve an existing collection value.我们尝试检索现有的集合值。 We do a null-check to see if indeed there was an existing collection value.我们进行空值检查以查看是否确实存在现有集合值。 If so, we add to it, and put it back into map.如果是这样,我们添加它,并将其放回地图。 If not, we instantiate a new collection, add to it, and put it into map.如果没有,我们实例化一个新的集合,添加到它,然后把它放到 map 中。

Set < DayOfWeek > assignedDays = workDaysForEachEmployee.get( alice );
if ( null == assignedDays )
{
    assignedDays = EnumSet.noneOf( DayOfWeek.class );
}
assignedDays.add( DayOfWeek.MONDAY );
workDaysForEachEmployee.put( alice , assignedDays );

When run.跑的时候。

workDaysForEachEmployee.toString() = {Employee[empNum=1, name=Alice]=[MONDAY]} workDaysForEachEmployee.toString() = {Employee[empNum=1, name=Alice]=[MONDAY]}

In modern Java 8+在现代 Java 8+

Modern Java brought lambdas , a compact way of passing code as an argument.现代 Java 带来了lambdas ,一种将代码作为参数传递的紧凑方式。

We can use lambdas and other new Java 8 functionality to reduce that code seen above to a single line.我们可以使用 lambdas 和其他新的 Java 8 功能将上面看到的代码减少到一行。

We call Map#computeIfAbsent .我们调用Map#computeIfAbsent In that call we pass a lambda.在那个调用中,我们传递了一个 lambda。 The lambda creates a collection in which to hold values. lambda 创建一个用于保存值的集合。 If the map already has an entry with our desired collection, the lambda is not executed.如果地图已经有一个包含我们想要的集合的条目,则不会执行 lambda。 One way or another, that computeIfAbsent method returns a collection value, a Set< DayOfWeek > in our case.无论如何, computeIfAbsent方法返回一个集合值,在我们的例子中是一个Set< DayOfWeek >

  • If the collection value exists (has already been mapped to a key), the value is returned.如果集合值存在(已经映射到某个键),则返回该值。 In our case, if the employee has already been put into the map with a set of days, that set is retrieved and returned.在我们的例子中,如果员工已经在一组天数内放入地图,则检索并返回该组。
  • If the collection value does not exist, a new collection is instantiated and returned.如果集合值不存在,则实例化并返回一个新集合。

In either case, pre-existing or freshly instantiated, we end up with a set in which to add our DayOfWeek object for the alice key.在任何一种情况下,无论是预先存在的还是新实例化的,我们最终都会得到一个集合,在其中添加我们的DayOfWeek对象作为alice键。

workDaysForEachEmployee
        .computeIfAbsent( alice , ( x -> EnumSet.noneOf( DayOfWeek.class ) ) )  // Returns a `Set< DayOfWeek >`, either a retrieved existing set, or a fresh new set. 
        .add( DayOfWeek.MONDAY );

workDaysForEachEmployee.toString() = {Employee[empNum=1, name=Alice]=[MONDAY]} workDaysForEachEmployee.toString() = {Employee[empNum=1, name=Alice]=[MONDAY]}

In that code above we instantiate a new collection by calling EnumSet.noneOf( DayOfWeek.class ) .在上面的代码中,我们通过调用EnumSet.noneOf( DayOfWeek.class )来实例化一个新集合。 The EnumSet class is an implementation of Set that is highly optimized for containing enum objects, using very little memory, and very fast to execute. EnumSet类是Set的一个实现,它针对包含枚举对象进行了高度优化,使用非常少的内存,并且执行速度非常快。 If you were using another type, such as a List you might use new ArrayList<>() .如果您使用其他类型,例如List ,您可能会使用new ArrayList<>()

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM