Как создать карту с двумя индексами?

У меня есть одна карта в java:

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

Я хочу получить Object на карте с помощью index1 и index2 в качестве поисковых запросов.

Ответ 1

Самый простой способ сделать это - использовать Guava Table, если вы хотите использовать стороннюю библиотеку.

Он работает следующим образом:

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

Вы можете добавить его в свой проект, выполнив следующие инструкции: Как добавить проект Guava в Eclipse


Если вы не хотите использовать Guava, у вас есть большая проблема. Если вы попытаетесь вставить элемент с новым первым ключом, вы должны убедиться, что сон уже существует. Это означает, что каждый раз, когда вы делаете put, вы должны получить innerMap, посмотреть, существует ли он, а затем создать его, если это не так. Вам нужно будет делать это каждый раз, когда вы вызываете Map.put. Также вы рискуете выбросить NullPointerException, если внутренняя карта не существует, когда вы вызываете get на внутренней карте.

Если вы это сделаете, следует обернуть ваш Map<String, Map<String, Object> во внешний класс для управления этими проблемами или использовать Java 8 computeIfAbsent. Но самый простой способ - просто использовать Table, как указано выше.

Если вы используете свой собственный класс вместо Table, это будет примерно так:

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);
  }
}

Вы бы использовали этот класс, выполнив следующие действия:

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

Обратите внимание, что этот ответ имеет намного меньше возможностей, чем версия Guava.

Ответ 2

Получение значения из Map

Если я понимаю ваш вопрос, то с индексом a и b, который может выглядеть (защищая от null с ternary или Условный оператор ? :),

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

Использование Общий тип

И вы можете быть более конкретным, например

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

Добавление значений в Map

Предполагая, что вы хотите добавить Something value к Map, который может быть выполнен с помощью чего-то вроде

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

Если вам не нужен регулярный доступ ко всей "строке", а просто быстрый доступ к каждой ячейке, вы можете использовать встроенный Map.Entry в качестве ключа:

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

В качестве альтернативы, если вы хотите пойти с чем-то сторонним, некоторые из них уже внедрили tuples для Java.

Если вы находитесь в ситуации, когда вы не можете легко загрузить стороннюю библиотеку, но вам не нравится семантика Map.Entry (которая написана в терминах key и value s), вы может написать собственный класс Pair, чтобы иметь тот же эффект.

Ответ 4

Как я понимаю, вы можете сделать так:

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

Ответ 5

Лучшее решение, вероятно, зависит от того, как эта карта предназначена для использования:

  • Используется ли он в ограниченной области действия или является частью общедоступного API?
  • Являются ли "индексы" всегда типа String или они должны быть общими?
  • Всегда ли это индексы два, или вам может понадобиться больше индексов позже?
  • ...

Прагматическое решение, сфокусированное на вопросе, как вы описали, будет представлять класс StringPair, который можно использовать для индексирования. Это избавляет вас от необходимости выполнять 2D-поиск внутренних карт (и возможных очищений, когда внутренние карты становятся пустыми!), Не требует каких-либо сторонних библиотек и является читабельным и эффективным.

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); 
    }
}

Конечно, возможны обобщения на Pair<T> или Tuple<S,T>, но это, похоже, не то, что вы искали...