2016-10-05 7 views
3

を使用して、2つのハッシュマップの間で動作を実行する私は、対応するキーの各対について、すなわちSTREAMのAPI

Map<String,Double> 

Map<Integer, Map<String,Double>> MAP_1 
Map<Integer, Map<String,Double>> MAP_2 

は、内側が内側マップとするフォームに2つのハッシュマップを持っています2つの内部マップ間の演算(例えば、減算)を実行したい。次に、この操作は、内側のマップの2つの対応するキーの間で実行される必要があります。 は、結果のマップは

Map<Integer,List<Double> RESULT 

サンプルマップは、外側のマップについては、だから、

MAP_1 
------------------------------ 
|  | 2016-10-02 10.0 | 
| ID1 | 2016-10-03 20.0 | 
|  | 2016-10-04 30.0 | 
------------------------------ 
|  | 2016-10-02 1.00 | 
| ID2 | 2016-10-03 2.00 | 
|  | 2016-10-04 3.00 | 
------------------------------ 

MAP_2 
------------------------------ 
|  | 2016-10-02 2.00 | 
| ID1 | 2016-10-03 3.00 | 
|  |      | 
------------------------------ 
|  | 2016-10-02 1.00 | 
| ID3 | 2016-10-03 2.00 | 
|  | 2016-10-04 3.00 | 
------------------------------ 

RESULT 
--------------- 
|  | 8.00 | 
| ID1 | 17.0 | 
|  |  | 
--------------- 

あるべきである、唯一のID1を考慮しなければなりません。操作には内部(共通)キー '2016-10-02'と '2016-10-03'が必要です。

ここに私の現在のコード

Set<Integer> common_keys = new LinkedHashSet<Integer>(map1.keySet()); 
    common_keys.retainAll(map2.keySet()); 
    System.out.println("Common keys: "+common_keys.toString()); 

    map1.forEach((k,v) -> { 

     Map<String,Double> submap_1 = new LinkedHashMap<String,Double>(v); 
     Map<String,Double> submap_2 = new LinkedHashMap<String,Double>(map2.get(k)); 

     Set<String> commons = new LinkedHashSet<String>(submap_1.keySet()); 
     commons.retainAll(submap_2.keySet()); 

     System.out.println(k+" : common days: "+commons); 


     List<Double> val1 = submap_1.keySet() 
       .stream() 
       .filter(c -> commons.contains(c)) 
       .map(c -> submap_1.get(c)) 
       .collect(Collectors.toList()); 

     List<Double> val2 = submap_2.keySet() 
       .stream() 
       .filter(c -> commons.contains(c)) 
       .map(c -> submap_2.get(c)) 
       .collect(Collectors.toList()); 

     List<Double> ABS = IntStream.range(0, val1.size()) 
       .mapToObj(i -> val1.get(i) - val2.get(i)) 
       .collect(Collectors.toList()); 

     diff_abs.put(k, ABS); 
     }); 

はJAVA 8' STREAMのAPIを使用することにより、これを行うためのシンプルかつスマートな方法はありますか?

事前

+2

なぜこのストリームAPIを使用したいですか?従来のコードが機能する場合は、従来のコードを使用してください。 – fge

+0

私はすでに自分のコードでストリームAPIを使用しています。私はそれが同じ結果を達成するためのよりコンパクトな方法が存在するかどうか疑問に思っていた – Fab

答えて

1

のおかげで上記のあなたの例のちょうどいくつかのポイント。 ListSet、およびMapの実装は、具体的には、一貫性のある注文(ただし、これは非常にまれに起こります)によって具体的に提供されるものが必要な場合を除き、ArrayListHashSet、およびです。ここで

は、私はこのに近づくだろう方法は次のとおりです。

public static <T, S> Map<T, List<Double>> method(
     Map<T, Map<S, Double>> map1, 
     Map<T, Map<S, Double>> map2) 
{ 
    Set<T> commonKeys = intersection(map1.keySet(), map2.keySet()); 

    return commonKeys.stream().collect(Collectors.toMap(
      Function.identity(), 
      key -> { 
       Map<S, Double> inner1 = map1.get(key); 
       Map<S, Double> inner2 = map2.get(key); 

       Set<S> innerCommonKeys = intersection(
         inner1.keySet(), 
         inner2.keySet()); 

         return innerCommonKeys.stream().map(innerKey -> 
           inner1.get(innerKey) - inner2.get(innerKey)) 
           .collect(Collectors.toList()); 
      })); 
} 

private static <T> Set<T> intersection(Set<T> set1, Set<T> set2) { 
    Set<T> intersection = new HashSet<T>(set1); 
    intersection.retainAll(set2); 
    return intersection; 
} 

そして、ここではそれが機能することを示すために "テスト" である:

public static void main(String[] args) 
{ 
    Map<String, Map<String, Double>> map1 = new HashMap<>(); 
    Map<String, Double> inner11 = new HashMap<>(); 
    inner11.put("2016-10-02", 10.0); 
    inner11.put("2016-10-03", 20.0); 
    inner11.put("2016-10-04", 30.0); 
    map1.put("ID1", inner11); 
    Map<String, Double> inner12 = new HashMap<>(); 
    inner12.put("2016-10-02", 1.00); 
    inner12.put("2016-10-03", 2.00); 
    inner12.put("2016-10-04", 3.00); 
    map1.put("ID2", inner12); 

    Map<String, Map<String, Double>> map2 = new HashMap<>(); 
    Map<String, Double> inner21 = new HashMap<>(); 
    inner21.put("2016-10-02", 2.00); 
    inner21.put("2016-10-03", 3.00); 
    map2.put("ID1", inner21); 
    Map<String, Double> inner22 = new HashMap<>(); 
    inner22.put("2016-10-02", 1.00); 
    inner22.put("2016-10-03", 2.00); 
    inner22.put("2016-10-04", 3.00); 
    map2.put("ID3", inner22); 

    System.out.println(method(map1, map2)); 
} 

出力:{ID1=[8.0, 17.0]}

+1

時々、私のアプリケーションでは、処理がキーの順序を混乱させないようにする必要があります。さらに、(キー、値)ペアをHashMapに追加すると、新しいキーが追加されます。この理由から私は 'Linked'実装を使用しました。 – Fab

3

Setの作成は不要です。もう1つのマップの同じキーに対して、これをnullのチェックと入れ替えます。さらに、マップがメソッド内で変更されないため、マップのコピーを作成する必要はありません。

public static <T, U> Map<T, List<Double>> merge(Map<T, Map<U, Double>> m1, Map<T, Map<U, Double>> m2) { 
    Map<T, List<Double>> result = new HashMap<>(); 
    m1.forEach((k, v) -> { 
     Map<U, Double> v2 = m2.get(k); 
     if (v2 != null) { 
      // both outer maps contain the same key 
      ArrayList<Double> list = new ArrayList<>(); 
      v.forEach((innerK, innerV) -> { 
       Double d = v2.get(innerK); 
       if (d != null) { 
        // matching key in both inner maps 
        list.add(innerV - d); 
       } 
      }); 
      // list.trimToSize(); 
      result.put(k, list); 
     } 
    }); 
    return result; 
} 
1

あなたが唯一の共通鍵を保持し、両方の値に関数を適用することにより、2つのマップをマージするための再利用可能な方法で作成することができます。そして、

public static <K,V,R> Map<K, R> merge(Map<K,V> map1, Map<K,V> map2, BiFunction<V,V,R> f) { 

    boolean hasOrder=map1.entrySet().spliterator().hasCharacteristics(Spliterator.ORDERED); 
    return map1.entrySet().stream() 
     .collect(hasOrder? LinkedHashMap<K,R>::new: HashMap<K,R>::new, (m,e)-> { 
      V v2 = map2.get(e.getKey()); 
      if(v2!=null) m.put(e.getKey(), f.apply(e.getValue(), v2)); 
     }, Map::putAll); 
} 

を、簡単に、ネストされたマップのマージを実装することができます二回メソッドを使用して:上記のコードは、場合にのみ場合LinkedHashMapを作成するために十分にスマートであることを

public static <K1, K2> Map<K1, List<Double>> merge(
    Map<K1, Map<K2, Double>> map1, Map<K1, Map<K2, Double>> map2) { 

    return merge(map1, map2, 
     (a,b) -> new ArrayList<>(merge(a, b, (x,y) -> x-y).values())); 
} 

注意を、最初のマップは、例えば、固有の順序を持​​っていますそれ自体がLinkedHashMapまたはSortedMapであり、それ以外の場合は平文HashMapです。とにかく、2番目のマップの順序は保持されません。元のコードで想定されているように、両方のマップの順序が同じであれば問題ありません。注文が異なる場合、明らかに両方の注文を保持する方法はありません。