[英]Dynamic grouping by specific attributes with Collection.stream
我試圖通過使用Java 8 Collection-Stream按多個屬性對對象列表進行分組。
效果很好:
public class MyClass
{
public String title;
public String type;
public String module;
public MyClass(String title, String type, String module)
{
this.type = type;
this.title = title;
this.module= module;
}
}
List<MyClass> data = new ArrayList();
data.add(new MyClass("1","A","B"));
data.add(new MyClass("2","A","B"));
data.add(new MyClass("3","A","C"));
data.add(new MyClass("4","B","A"));
Object result = data.stream().collect(Collectors.groupingBy((MyClass m)
-> m.type, Collectors.groupingBy((MyClass m) -> m.module)));
但我想使其更具動態性。 我只想指定一個應該用於GroupBy的字符串數組(或列表)。
就像是:
Object groupListBy(List data, String[] groupByFieldNames)
{
//magic code
}
我想打電話給:
groupListBy(data, new String[]{"type","module"});
像我的示例一樣,如何使groupBy-Method更動態?
使代碼更具動態性的主要問題是,您事先不知道要分組多少個元素。 在這種情況下,最好按所有元素的List
進行分組。 之所以可行,是因為如果兩個列表的所有元素均相同且順序相同,則它們相等。
在這種情況下,我們將按每個數據類型和模塊組成的列表進行分組,而不是按類型和模塊進行分組。
private static Map<List<String>, List<MyClass>> groupListBy(List<MyClass> data, String[] groupByFieldNames) {
final MethodHandles.Lookup lookup = MethodHandles.lookup();
List<MethodHandle> handles =
Arrays.stream(groupByFieldNames)
.map(field -> {
try {
return lookup.findGetter(MyClass.class, field, String.class);
} catch (Exception e) {
throw new RuntimeException(e);
}
}).collect(toList());
return data.stream().collect(groupingBy(
d -> handles.stream()
.map(handle -> {
try {
return (String) handle.invokeExact(d);
} catch (Throwable e) {
throw new RuntimeException(e);
}
}).collect(toList())
));
}
代碼的第一部分將字段名稱數組轉換為MethodHandle
的List
。 對於每個字段,都為該字段檢索一個MethodHandle
:這是通過從MethodHandles.lookup()
獲取一個查找並使用MethodHandles.lookup()
查找給定字段名稱的句柄來findGetter
:
產生方法句柄,以提供對非靜態字段的讀取訪問權限。
其余代碼創建分類器,以將其分組。 在數據實例上調用所有句柄以返回String
值列表。 該Stream
被收集到List
以用作分類器。
樣例代碼:
public static void main(String[] args) {
List<MyClass> data = new ArrayList<>();
data.add(new MyClass("1", "A", "B"));
data.add(new MyClass("2", "A", "B"));
data.add(new MyClass("3", "A", "C"));
data.add(new MyClass("4", "B", "A"));
System.out.println(groupListBy(data, new String[] { "type", "module" }));
}
輸出:
{[B, A]=[4], [A, B]=[1, 2], [A, C]=[3]}
重寫MyClass.toString()
以僅返回title
。
除了名稱列表,您還可以考慮提供功能列表(強制一個)以對元素進行分組。
這些函數應該將MyClass
的元素映射到對象,因此可以使用Function<MyClass, ?>
。
private static Map<List<Object>, List<MyClass>> groupListBy(List<MyClass> data, Function<MyClass, ?> mandatory, Function<MyClass, ?>... others) {
return data.stream()
.collect(groupingBy(cl -> Stream.concat(Stream.of(mandatory), Stream.of(others)).map(f -> f.apply(cl)).collect(toList())));
}
還有一些通話示例:
groupListBy(data, m -> m.type); //group only by type
groupListBy(data, m -> m.type, m -> m.module); //group by type and by module
當然,您可以使此方法通用,以便它返回Map<List<Object>, List<U>>
,其函數類型為U -> Object
。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.