How can I make a Map with two indexes?

浪尽此生 提交于 2019-12-22 03:22:47

问题


I have one Map in java like this:

Map<String index1, Map<String index 2, Object obj>> map = new HashMap<>();

I want to get my Object in the map by using index1 and index2 as lookups.


回答1:


The easiest way to do this would be to use Guava's Table, if you're willing to use a third party library.

It works like this:

Table<String, String, Object> table = HashBasedTable.create();
table.put(index1, index2, obj);
Object retrievedObject = table.get(index1, index2);

You can add it to your project by following these instructions: How to add Guava to Eclipse project


If you don't want to use Guava, you have a big problem. If you try to insert an element with new first key, you have to make sure the innermap already exists. This means, every time you do put, you have to retrieve the innerMap, see if it exists, and then create it if it does not. You will have to do this every time you call Map.put. Also, you risk throwing a NullPointerException if the inner map doesn't exist when you call get on the inner map.

If you do this, should wrap your Map<String, Map<String, Object> in an outer class to manage these problems, or use Java 8's computeIfAbsent. But the easiest way is to just use Table as above.

If you make your own class to use instead of Table, it would be something like:

public class DoubleMap<R, C, V> {
  private final Map<R, Map<C, V>> backingMap;

  public DoubleMap() {
    this.backingMap = new HashMap<>();
  }

  public V get(R row, C column) {
    Map<C, V> innerMap = backingMap.get(row);
    if(map == null) return null;
    else return innerMap.get(column);
  }

  public void put(R row, C column, V value) {
    Map<C, V> innerMap = backingMap.get(row);
    if(innerMap == null) {
      innerMap = new HashMap<C, V>();
      backingMap.put(row, innerMap);
    }
    innerMap.put(column, value);
  }
}

You would use this class by doing:

DoubleMap<String, String, Object> map = new DoubleMap();

Note that this answer has a lot less features than the Guava version.




回答2:


Getting a Value from a Map

If I understand your question, then with an index a and b that might look like (guarding against null with a ternary or Conditional Operator ? :),

Object obj = (map.get("a") == null) ? null : map.get("a").get("b");

Using a Generic Type

And you might be more specific, like

Map<String, Map<String, Something>> map = new HashMap<>();
Something s = (map.get("a") == null) ? null : map.get("a").get("b");

Adding values to the Map

Assuming you want to add your Something value to the map that could be done with something like,

Map<String, Map<String, Something>> map = new HashMap<>();
if (map.get("a") == null) {
    map.put("a", new HashMap<>());
}
map.get("a").put("b", value);



回答3:


If you don't need regular access to the entire "row", but just quick access to each cell you can use the built-in Map.Entry as your key:

Map<Map.Entry<String, String>, Object> table = new Map<>();
table.put(new Map.SimpleEntry("index1", "index2"), "Hello world");

Alternatively, if you're willing to go with something third-party, several someones have already implemented tuples for Java.

If you are in a situation where you cannot pull in a third-party library easily, but you don't like the semantics of Map.Entry (which is written in terms of keys and values) you can write your own Pair class to have the same effect.




回答4:


As my understanding, you can do like:

Map<String, Map<String, Object> map= new HashMap();
Map<String, Object> subMap = map.get("index1");
if(subMap != null) {
    Object obj = subMap.get("index2");
}



回答5:


The best solution probably depends on how this map is intended to be used:

  • Is it used in a limited scope, or is it part of a public API?
  • Are the "indices" always of type String, or do they have to be generic?
  • Will it always be two indices, or may you need more indices later?
  • ...

A pragmatic solution focussed on the question as you described it would be to introduce a StringPair class that can be used for indexing. This saves you from the hassle of doing 2D-lookups of inner maps (and possible cleanups when the inner maps become empty!), does not require any third-party libraries, and is readable and efficient.

import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Objects;

public class StringPairMapTest
{
    public static void main(String[] args)
    {
        Map<StringPair, Object> map = new LinkedHashMap<StringPair, Object>();

        map.put(StringPair.of("A","B"), 12);
        map.put(StringPair.of("C","D"), 34);

        System.out.println(map.get(StringPair.of("A","B")));
        System.out.println(map.get(StringPair.of("C","D")));
        System.out.println(map.get(StringPair.of("X","Y")));
    }
}

class StringPair
{
    private final String s0;
    private final String s1;

    static StringPair of(String s0, String s1)
    {
        return new StringPair(s0, s1);
    }

    private StringPair(String s0, String s1)
    {
        this.s0 = s0;
        this.s1 = s1;
    }

    @Override
    public String toString()
    {
        return "("+s0+","+s1+")";
    }

    @Override
    public int hashCode()
    {
        return Objects.hash(s0, s1);
    }

    @Override
    public boolean equals(Object obj)
    {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        StringPair other = (StringPair) obj;
        return Objects.equals(s0, other.s0) && Objects.equals(s1, other.s1); 
    }
}

Generalizations to a Pair<T> or Tuple<S,T> would be possible, of course, but this did not seem to be what you have been looking for...



来源:https://stackoverflow.com/questions/32151775/how-can-i-make-a-map-with-two-indexes

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!