質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
Java

Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。

Q&A

解決済

1回答

2597閲覧

POJOをキーとして2つのオブジェクトをマージ

lupus_dingo

総合スコア257

Java

Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。

0グッド

0クリップ

投稿2019/05/07 11:47

編集2019/05/07 13:09

お世話になっております。

以下のようにするとリストから任意のキーでマップに変換できると思います。

java

1Map<String, List<ChildA>>map=list.stream().collect(Collectors.groupby(ChildA::getId));

このマップのキーをstringからParentにし、
かつ変換後mapに対してParentをキーにChildBをマージしたいのですが、レガシーなfor文を使う以外にステップ数を抑えて可読性が上がる方法はないでしょうか?

java

1// 変換前のイメージ 2class BeforeParent { 3 private Parent parent; 4 private ChildA childA; // 中身はBと同様 5} 6class childB { 7 private Integer pk1; // parentのpk 8 private String pk2; // parentのpk 9 private String pk3; 10 private String data1; 11 private String data2; 12}

java

1// 変換後のイメージ 2class After { 3 private Parent parent; 4 private List<ChildA> childA; 5 private List<ChildB> childB; 6} 7

上記のとおり、java8が使用可能です。

やりたいのは、
M:Nのデータを結合して一括抽出した後で、M:Oとなるデータを同様に抽出し、
Mごとにループ処理を行うということです。

追記
抽出したデータを編集することはありません(→イミュータブル?)。
equalsはオーバーライドせずに、parentが持つ複合キー(全て基本型)それぞれに対してforループ内でequalsチェックする想定でした。

→ふと、思いましたが、equalsをオンバーライドすれば上記のstreamで行けそう?(^^;

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

xebme

2019/05/07 12:38

ParentはイミュータブルかつhashCode(),equals()が適切にオーバーライドされていないと、マージする際にふたつのParentの一致判定ができないと思われますが、それはできているという前提ですね。一方にキーエントリが存在して、他方に存在しない場合は想定しますか。できれば、追記をお願いします。
lupus_dingo

2019/05/07 12:56

回答ありがとうございます。追記しました。
xebme

2019/05/07 13:14

Parentがキーなので、<M:N>.forEach( .... <M:O>.merge() ) というような処理を行うなら、Parentの equals()/hashCode()がオーバーライドされていないと、ふたつのMapのキー(違うインスタンスの同じParentのキーどおし)を突合できないと思っているだけですが、Stringをキーにしておくことも含めて回答者が考えることだと理解しました。
lupus_dingo

2019/05/07 13:18

そうですね、equalsをオーバーライドするだけの話な気がしてきました…
guest

回答1

0

ベストアンサー

参考にした情報 Merging Two Maps with Java このなかの、Stream を結合する方法を参考にしました。入力は2つの Map 、出力は List です。

Java

1public static List<After> merge(Map<Parent,List<ChildA>> mapA, Map<Parent,List<ChildB>> mapB) { 2 return Stream.concat( 3 mapA.entrySet().stream().map(e -> new After(e.getKey(),e.getValue(),null)), 4 mapB.entrySet().stream().map(e -> new After(e.getKey(),null,e.getValue())) 5 ).collect(Collectors.groupingBy(e -> e.getParent())) 6 .values().stream() 7 .map(l -> l.stream().reduce(new After(),(a,b)-> new After(a,b))) 8 .collect(Collectors.toList()); 9}

Afterにコンストラクタが必要なので追加しました。Parentの取得メソッドも追加。(フィールドが private なので)

Java

1class After { 2 private Parent parent; 3 private List<ChildA> childA; 4 private List<ChildB> childB; 5 6 public After(Parent parent, List<ChildA> childA, List<ChildB> childB) { 7 this.parent = parent; 8 this.childA = (childA == null) ? new ArrayList<>() : childA; 9 this.childB = (childB == null) ? new ArrayList<>() : childB; 10 } 11 public After(After a, After b) { // reduce マージ用コンストラクタ 12 this((a.parent!=null)?a.parent:b.parent,a.childA,a.childB); 13 this.childA.addAll(b.childA); 14 this.childB.addAll(b.childB); 15 } 16 public After() { this(null,null,null); } // reduce 初期値 17 18 public Parent getParent() { return this.parent; } // Parent の取得 19 20}

POJO をキーにするということなので、Parentの equals()/hahsCode() をオーバーライドしました。自作オブジェクトを HashMap に格納するには必要です。equals()/hahsCode() に必要なフィールドは、final 変更できません。変更すると HashMap から取り出せなくなるから。意味がわからなければ、ご自分で調べてください。

Java

1class Parent { 2 private final Integer pk1; // parentのpk 3 private final String pk2; // parentのpk 4 5 public Parent(Integer pk1, String pk2) { 6 this.pk1 = pk1; 7 this.pk2 = pk2; 8 } 9 10 @Override 11 public boolean equals(Object obj) { 12 if (obj == this) { 13 return true; 14 } 15 if (!(obj instanceof Parent)) { 16 return false; 17 } 18 Parent right = (Parent)obj; 19 if (!this.pk1.equals(right.pk1)) { 20 return false; 21 } 22 if (!this.pk2.equals(right.pk2)) { 23 eturn false; 24 } 25 return true; 26 } 27 28 @Override 29 public int hashCode() { 30 return pk1.hashCode() + pk2.hashCode(); 31 } 32 33}

After のマージコンストラクター (2019-05-08)

Map.merge()を使うと map に格納する型が合わず型変換が必要になる。After のコンストラクターにマージ機能を持たせるほうが凝集度が高くなるし、畳み込みにより2件以上のマージができると判断。

キーオブジェクトの equals()/hashCode()、immutable (2019-05-08)

参考書は『Effective Java』 第1版〜第3版 のどれでもよい、読んでみてください。

Mapのキーについての議論
HashMap/Setにまつわる2つの事
Immutable objects and hashmap keys

Java の基礎として知ておくべき。調べたうえで疑問が残るなら、別の質問をポストしてください。

hashCode() を Effective Java にあわせる (2019-05-09)

J. Broch さんはこれがベストではないと断っていますが、Effective Java にあわせます。

Java

1 @Override 2 public int hashCode() { 3 int result = 17; 4 result = result * 31 + pk1.hashCode(); 5 result = result * 31 + pk2.hashCode(); 6 return result; 7 }

pk1、pk2 はプライマリキーなので null になりえないことが前提です。また final な変数ゆえに1度だけ計算してキャッシュすることもできます。

投稿2019/05/07 17:14

編集2019/05/09 09:52
xebme

総合スコア1081

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

lupus_dingo

2019/05/15 16:18

回答ありがとうございます。 非常に参考になりました。 effective javaは本棚の肥やしにしているので引っ張りだして読んでみます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問