重构Map的Map Map

时间:2016-03-27 07:07:41

标签: java

我正在审核项目的旧代码,并使用The following cache components were processed: * cache (yii\\caching\\FileCache) Map Map(3层地图)的Map获得了数据结构:

// data structure
Map<String, Map<String, Map<String, List<String>>>> tagTree 
            = new HashMap<String, Map<String,Map<String,List<String>>>>();

从Map中获取值(我认为这是很好的部分)

// fetch at tag values
List<String> tagList1 = tagTree.get("Java").get("Active").get("Tags");
List<String> tagList2 = tagTree.get("Java").get("Latest").get("SubTags");

将值放在Map中(有点复杂且容易出错)

// put values
Map<String, Map<String, List<String>>> javaLangMap = new HashMap<String, Map<String, List<String>>>();
Map<String, List<String>> javaStatusMap = new HashMap<String, List<String>>();
List<String> javaTagList = new ArrayList<String>();

javaTagList.add("Java-OOP");
javaTagList.add("Java-Variables");
// put tag list
javaStatusMap.put("Tags", javaTagList);
// put status-wise tag
javaLangMap.put("Active", javaStatusMap);
// put language-wise tag
tagTree.put("Java", javaLangMap);

目前,这用于维护以下结构

TagLanguage - &gt; TagStatus - &gt; TagType - &gt;标记表

我计划重构此地图,因为其他开发者很难阅读。

请分享您的想法如何通过考虑以下案例来做到这一点:

  • 可以在运行时更改所有四个图层。
  • 所有级别都应该 访问
  • 需要内存中的解决方案,即不要使用数据库表层次结构。

6 个答案:

答案 0 :(得分:11)

如果您只想访问数据结构的最后一级,可以使用(d % 1) == 0Multimap<K,V>是来自Guava的数据结构,基本上是一个更好的Multimap<Triple<String,String,String>,String>Triple<L,M,R>是来自Apache Commons Lang3的3元素元组数据结构,Map<K,Collection<V>>并实现Comparable

你可以像这样声明你的标签树:

equals

然后像这样填写:

Multimap<Triple<String, String, String>, String> tagTree = HashMultimap.create();

或者:

tagTree.put(Triple.of("Java", "Active", "Tags"), "Java-OOP");
tagTree.put(Triple.of("Java", "Active", "Tags"), "Java-Variables");

然后从中获取您的价值:

tagTree.putAll(Triple.of("Java", "Active", "Tags"), Arrays.asList("Java-OOP", "Java-Variables"));

这是另一个可能适合您的粗略解决方案,可以使用1,2或3个键:

Set<String> values = tagTree.get(Triple.of("Java", "Active", "Tags"));

你可以这样使用它:

import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

import org.apache.commons.lang3.tuple.Pair;
import org.apache.commons.lang3.tuple.Triple;

import com.google.common.collect.HashMultimap;
import com.google.common.collect.Multimap;

public class ThreeLevelMap<K1, K2, K3, V> {
    private Map<K1, Map<K2, Multimap<K3, V>>> firstLevelMap = new HashMap<>();
    private Map<Pair<K1, K2>, Multimap<K3, V>> secondLevelMap = new HashMap<>();
    private Multimap<Triple<K1, K2, K3>, V> thirdLevelMap = HashMultimap.create();

    public void put(K1 key1, K2 key2, K3 key3, V value) {
        thirdLevelMap.put(Triple.of(key1, key2, key3), value);

        final Pair<K1, K2> secondLevelKey = Pair.of(key1, key2);
        Multimap<K3, V> secondLevelContainer = secondLevelMap.get(secondLevelKey);
        if (secondLevelContainer == null) {
            secondLevelContainer = HashMultimap.create();
            secondLevelMap.put(secondLevelKey, secondLevelContainer);
        }
        secondLevelContainer.put(key3, value);

        Map<K2, Multimap<K3, V>> firstLevelContainer = firstLevelMap.get(key1);
        if (firstLevelContainer == null) {
            firstLevelContainer = new HashMap<>();
            firstLevelMap.put(key1, firstLevelContainer);
        }
        firstLevelContainer.put(key2, secondLevelContainer);
    }

    public Collection<V> get(K1 key1, K2 key2, K3 key3) {
        return thirdLevelMap.get(Triple.of(key1, key2, key3));
    }

    public Multimap<K3, V> get(K1 key1, K2 key2) {
        return secondLevelMap.get(Pair.of(key1, key2));
    }

    public Map<K2, Multimap<K3, V>> get(K1 key1) {
        return firstLevelMap.get(key1);
    }
}

我说这很粗糙,因为:

  • ThreeLevelMap<String, String, String, String> tlm = new ThreeLevelMap<>(); tlm.put("Java", "Active", "Tags", "Java-OOP"); tlm.put("Java", "Active", "Tags", "Java-Variables"); Map<String, Multimap<String, String>> firstLevelMap = tlm.get("Java"); Multimap<String, String> secondLevelMap = tlm.get("Java", "Active"); Collection<String> tags = tlm.get("Java", "Active", "Tags"); 方法返回的地图是可修改的
  • 我没有实施get方法
  • 我没有经常测试

答案 1 :(得分:2)

您可以创建将保存数据结构的类

public class A {
    Map<String, List<String>> map;
}

public class B {
    Map<String, A> map;
}

Map<String, B> tagTree;

答案 2 :(得分:2)

我认为这不是一个糟糕的解决方案。 它只是一个树表示,对于每棵叶子都在3级的树。 如果不是这种情况(不同的叶级别等),则必须构建树类结构。

但我要改变的是将所有内容放在中,并使用get和set方法,包括 null-checks

在下面的代码中,add方法处理中间级别映射的容易出错的处理,而get检查中间级别的空值:

public class TreeStructure {
  Map<String, Map<String, Map<String, List<String>>>> tagTree 
            = new HashMap<String, Map<String,Map<String,List<String>>>>();

  // ... Constructor ...

  // This method adds all intermediate levels if not existing
  public void add(String level1, String level2, String level3) {
    String l1 = tagTree.get(level1);
    if(l1 == null)
      tagTree.put(level1, new HashMap<String, Map<String, List<String>>>());
    l1 = tagTree.get(level1);
    String l2 = l1.get(level2),
    if(l2 == null)
      tagTree.put(level2, new Map<String, List<String>>(););
    l2 = l1.get(level2);
    String l3 = l2.get(level3);
    if(l3 == null) l2.add(level3, new ArrayList<>());
  }

  // This method checks, if every intermediate level existed
  // Otherwise, get() returns null, and the next get() would fail
  public String get(String level1, String level2, String level3) {
    String l1 = tagTree.get(level1);
    if(l1 == null)
      return null;
    String l2 = l1.get(level2),
    if(l2 == null)
      return null;
    l2 = l1.get(level2);
    String l3 = l2.get(level3);
    return l3;
  }
}

(未经测试的代码)

答案 3 :(得分:2)

使用3元组的地图:

class Tuple3<A,B,C> {
   private A a;
   private B b;
   private C c;
   // getters, setters, constructor
   // make sure equals() and hashCode() are okay
}

但请注意,地图的地图可以通过查看外部地图在O(1)中告诉您是否有某些元素的条目。然而,使用元组解决方案,您只能使用完整密钥。

答案 4 :(得分:1)

我认为没有理由重构这个Map结构。但是如果可能的话,将这个Map封装在另一个class中并为其他开发人员提供一个干净的界面可能是个好主意。

...

public void addTag(String language, String status, String tag)

public void removeTag(String language, String status, String tag)

public List<String> getTags(String language, String status)

...

答案 5 :(得分:1)

我喜欢上面提到的大多数解决方案。我认为设计更简单有效 - 它将更加可支持。

因此,我使用基础知识 - 普通对象组合来重构代码。

package design;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

class JavaTag{
  private String tags;

  JavaTag(String tags){
    this.tags = tags;
  }
 }

class JavaTagStatusList{
  private ArrayList<JavaTag> tagList = new ArrayList<JavaTag>();

  JavaTagStatusList(){

  }

  public void addJavaTag (JavaTag tagObj){
    if (tagObj != null){
        tagList.add(tagObj);
    }
  }
}

class JavaTagStatusMap {
    private HashMap<String, JavaTagStatusList> tagStatusMap = new HashMap<String, JavaTagStatusList>();

    JavaTagStatusMap(){
    }


    public void addTagStatusEntry(String status, JavaTag obj){
      if (tagStatusMap.containsKey(status)){
        tagStatusMap.get(status).addJavaTag(obj);
      }
      else {
        JavaTagStatusList statusList = new JavaTagStatusList();
        statusList.addJavaTag(obj);
        tagStatusMap.put(status, statusList);
    }
  }
}

主:

public class MapofMapRefactor {

    public static void main(String[] args) {
      JavaTag tag1 = new JavaTag("Java-OOP");
      JavaTag tag2 = new JavaTag("Java-Variables");

      JavaTagStatusMap statusMap = new JavaTagStatusMap();
      statusMap.addTagStatusEntry("Active", tag1);
      statusMap.addTagStatusEntry("Active", tag2);

      // HashMap of Java Lang Map
      HashMap<String, JavaTagStatusMap> javaLanguageMap = new HashMap<String, JavaTagStatusMap>();
      javaLanguageMap.put("Java", statusMap);   

  }

}