2016-07-12 12 views
5

ストリームを使用して、あまりエレガントでないコードをリファクタリングしようとしています。私は文字列とMyObjectsを含むHashMapを持っており、現在はそのようにforループを使用して、それを反復処理:私はIDのみを気にしているのでストリームを使用したグループ化に基づいて、コレクションで2つの異なる機能を実行するにはどうすればよいですか?

Map<String, MyObject> map = new HashMap<>(); 
Map<String, MyObject> objectsToAdd = new HashMap<>(); 


for(MyObject object : map.values()){ 
     String idToAdd = object.getConnectedToId(); 

     if(StringUtils.isEmpty(idToAdd) { 
      continue; 
     } 

     if(idToAdd.substring(0,1).equals("i")){ // connected to an ICS 
      MyObject newObject = service1.someMethod(idToAdd); 

      if(newObject != null) { 
       objectsToAdd.put(newObject.getId(), newObject); 
      } 
     } else if (idToAdd.substring(0,1).equals("d")){ // connected to a device 
      MyObject newObject = service2.someMethod(idToAdd); 
      if(newObject != null) { 
       objectsToAdd.put(newObject.getId(), newObject); 
      } 
     } 

    } 

    map.putAll(objectsToAdd); 

、私が続く、IDのみを取得するにはマップの操作を使用して開始しました空のフィルタを削除するフィルタ操作

次の部分は私が問題を抱えています。

 map.values().stream() 
      .map(myObject -> myObject.getConnectedToId()) // get a map of all the ids 
      .filter(StringUtils::isNotEmpty) // filter non empty ones 
      .collect(
       Collectors.mapping(
        MyObject::getId, 
        Collectors.toList())), 
         Collectors.groupingBy(
          s -> s.substring(0,1)); 

このリンクストリームコレクターを使用して削減を手伝ってくれました:私はグループの項目は、IDの最初の文字に基づいて、私はこれで終わったが、ことができるように、私が試した最初のものは、コレクターgroupingBy操作を使用していましたStream Reduction

このコードには少なくとも次の2つの問題があります.1)収集はterminal operationで終了し、まだ完了していません.2)元のオブジェクトはまだ必要ですが、現在はconnectedToIdsのマップ。

Q1)IDの最初の文字に基づいてオブジェクトをグループ化できる中間操作はありますか?

Q2)コレクションをIDだけに縮小することなくこれを行うにはどうすればよいですか?

Q3)最後に、コレクションがグループ化されると(2つになります)、元のコードのように各グループで別々の機能を実行するにはどうすればよいですか?


最終解決(おかげで助けを& @Flownを@Holgerする)

Map<Character, Function<String, MyObejct>> methodMapping = new HashMap<>(); 
    methodMapping.put('i', service1::method1); 
    methodMapping.put('d', service2::method2); 

    Map<String, MyObject> toAdd = map.values().stream().map(MyObject::getConnectedToId) 
     .filter(StringUtils::isNotEmpty) 
     .map(id -> methodMapping.getOrDefault(id.charAt(0), i -> null).apply(id)) 
     .filter(Objects::nonNull) 
     .collect(Collectors.toMap(MyObject::getId, Function.identity(), (mo1, mo2) -> mo2)); 

    map.putAll(toAdd); 

同時変更例外を回避するために実行している間、それは一時的なマップ内のオブジェクト最初の記憶しておく必要がありますストリーム操作を完了したら、最後のマップに追加します。

+1

私はマップのキーが 'MyObject'の' id'だとしますか?私。 – Flown

+0

はい、HashMapのキーはMyObjectのIDです。わかりやすさのために質問にマイナーな編集を加えました。idToAddはオブジェクトのIDではなくmyObjectのconnectedToIdです。 – Kristina

答えて

3

あなたのStreamアプローチとあなたの共通のアプローチは、戻りタイプに関して非常に異なります。したがって、以前のアプローチをStream APIに変えました。

コードの一部を減らすには、最初にMap<Character, Function<String, MyObject>>を作成して、マッピング手順で簡潔な検索を行う必要があります。

パイプラインはどのように機能するの
Map<Character, Function<String, MyObject>> serviceMapping = new HashMap<>(); 
serviceMapping.put('i', service1); 
serviceMapping.put('d', service2); 


は次のようになりますか?

  1. マップMyObject - >MyObject::getConnectedToId
  2. フィルタ空Strings
  3. serviceMapで検索を実行します。それが存在する場合、最後のステップは、それが

Map<String, MyObject> toAdd = map.values().stream().map(MyObject::getConnectedToId) 
    .filter(StringUtils::isEmpty) 
    .map(id -> serviceMapping.getOrDefault(id.charAt(0), i -> null).apply(id)) 
    .filter(Objects::nonNull) 
    .collect(Collectors.toMap(MyObject::getId, Function.identity(), (mo1, mo2) -> mo2)); 
map.putAll(toAdd); 

右の抽出機能を提供することで、結果を収集することですFunction<String, MyObject>、他id -> null

  • フィルタnull
  • を返しますforEach操作を使用して計算値を直接mapに加算することもできます。

    map.values().stream().map(MyObject::getConnectedToId) 
        .filter(StringUtils::isEmpty) 
        .map(id -> serviceMapping.getOrDefault(id.charAt(0), i -> null).apply(id)) 
        .filter(Objects::nonNull) 
        .forEach(mo -> map.put(mo.getId(), mo)); 
    
  • +0

    mo1とmo2はmethod1とmethod2としますか? また、最後の収集操作で、静的コンテキストから非静的メソッドを参照することはできません。 – Kristina

    +2

    @Kristina既に存在する値が衝突した場合、デフォルトの 'Collectors :: toMap'は例外をスローします。したがって、どの要素が取られるか、新しい値か古い値( ''(mo1、mo2) - > mo2')を決定するマージ関数を提供する必要があります。 – Flown

    +0

    @Holger 'map'オペレーションが動作するまでのすべてです。 'MyObject'を返す関数が必要なので、' serviceMapping'を一度修正しなければなりませんでした。 'Map <文字、関数 methodMapping = new HashMap <>(); methodMapping.put( 'i'、service1 :: getMOById); methodMapping.put( 'd'、service2 :: getMOById); ' これは正しく見えますか?メソッドはこの場合呼び出されないようで、代わりに 'map'オペレーションは結果を返しません。 – Kristina