Google PlacesAPIから取得したGooglePlaceSummaryオブジェクトのリストがあります。それらをGoogleプレイスIDで収集してグループ化しますが、要素の順序も保持したいと思います。私がうまくいくと思ったのは:
_Map<String, List<PlaceSummary>> placesGroupedByPlaceId =
places.stream()
.collect(Collectors.groupingBy(
PlaceSummary::getPlaceId,
LinkedHashMap::new,
Collectors.mapping(PlaceSummary::getPlaceId, toList())
));
_
しかし、それはコンパイルすらしません。 コレクターに関するJava APIドキュメント によるとそうあるべきです。
以前、私はこのコードを持っていました:
_ Map<String, List<PlaceSummary>> placesGroupedByPlaceId = places.stream()
.collect(Collectors.groupingBy(PlaceSummary::getPlaceId));
_
ただし、StreamsAPIの標準の.collect()
は、後続のHashMap
の要素の順序を保持しません(明らかにHashMap
sは順序付けされていないため)。マップが各バケットの挿入順序でソートされるように、出力をLinkedHashMap
にしたいと思います。
ただし、私が提案したソリューションはコンパイルされません。まず、関数ではないと言っているので、_PlaceSummary::getPlaceId
_を認識しません-私はそれを知っていますが。次に、_LinkedHashMap<Object, Object>
_をMに変換できないと表示されます。Mは汎用コレクションであると想定されているため、受け入れる必要があります。
Java Stream APIを使用してリストをLinkedHashMap
に変換するにはどうすればよいですか?それを行うための簡潔な方法はありますか?理解するのが難しすぎる場合は、古いものに頼ることができます学校のJava8以前のメソッド。
ListをLinkedHashMapに変換する際の別のStack Overflowの回答 があることに気付きましたが、具体的に繰り返し処理しているオブジェクトを「this」収集する必要があるため、これには解決策がありません。
あなたは本当にあなたが望むものに近づいています:
_Map<String, List<PlaceSummary>> placesGroupedByPlaceId =
places.stream()
.collect(Collectors.groupingBy(
PlaceSummary::getPlaceId,
LinkedHashMap::new,
Collectors.mapping(Function.identity(), Collectors.toList())
));
_
_Collectors.mapping
_メソッドでは、場所IDではなくPlaceSummary
インスタンスを指定する必要があります。上記のコードでは、Function.identity()
を使用しました。このコレクターは値を作成するために使用されるため、場所自体(IDではなく)を累積する必要があります。
Collectors.toList()
の代わりにCollectors.mapping(Function.identity(), Collectors.toList())
を直接書き込むことができることに注意してください。
これまでのコードは、実際には_Map<String, List<String>>
_を作成しているため、コンパイルされません。各IDのIDを蓄積しています(これは非常に奇妙です)。
これは一般的なメソッドとして記述できます。
_private static <K, V> Map<K, List<V>> groupByOrdered(List<V> list, Function<V, K> keyFunction) {
return list.stream()
.collect(Collectors.groupingBy(
keyFunction,
LinkedHashMap::new,
Collectors.toList()
));
}
_
次のように使用します。
_Map<String, List<PlaceSummary>> placesGroupedById = groupByOrdered(places, PlaceSummary::getPlaceId);
_
最終的なコレクターについて少し混乱したと思います。これは、各マップ値に必要なものを表すだけです。元のオブジェクトのリストが必要なだけなので、セカンダリmapping
コレクターを用意する必要はありません。
Map<String, List<PlaceSummary>> placesGroupedByPlaceId =
places.stream()
.collect(Collectors.groupingBy(PlaceSummary::getPlaceId,
LinkedHashMap::new,
Collectors.toList()));
順序を維持しながらグループ化が必要で、関数(削減)を適用する場合は、おそらくカウントして、このようなものを使用します。
final Map<Integer,Long>map=stream.collect(Collectors.groupingBy(function
,LinkedHashMap::new
,Collectors.collectingAndThen(Collectors.counting(),Function.identity()))
)
/**
* I have written this code more generic, if you want then you can group based on any *
* instance variable , id, name etc via passing method reference.
**/
class Student {
private int id;
private String name;
public Student(int id, String name) {this.id = id;this.name = name;}
/**
* @return the id
*/
public int getId() {return id;}
/**
* @param id
* the id to set
*/
public void setId(int id) {this.id = id;}
/**
* @return the name
*/
public String getName() {return name;}
/**
* @param name
* the name to set
*/
public void setName(String name) {this.name = name;}
}
public class StudentMain {
public static void main(String[] args) {
List<Student> list = new ArrayList<>();
list.add(new Student(1, "Amit"));
list.add(new Student(2, "Sumit"));
list.add(new Student(1, "Ram"));
list.add(new Student(2, "Shyam"));
list.add(new Student(3, "Amit"));
list.add(new Student(4, "Pankaj"));
Map<?, List<Student>> studentById = groupByStudentId(list,
Student::getId);
System.out.println(studentById);
Map<?, List<Student>> studentByName = groupByStudentId(list,
Student::getName);
System.out.println(studentByName);
}
private static <K, V> Map<?, List<V>> groupByStudentId(List<V> list,
Function<V, K> keyFunction) {
return list.stream().collect(
Collectors.groupingBy(keyFunction, HashMap::new,
Collectors.toList()));
}
}