我可以按属性分组并映射到Java 8中的新对象吗?

时间:2018-12-18 11:52:46

标签: java java-8

嗨,我有一个对象数组,其中类别和子类别详细信息位于同一对象中。就像

public class MyObject {
    String categoryCode;
    String categeoryCodeDescription;
    String subCategoryCode;
    String subCategoryCodeDescription;

    public MyObject(String categoryCode, String categeoryCodeDescription, String subCategoryCode, String subCategoryCodeDescription) {
        this.categoryCode = categoryCode;
        this.categeoryCodeDescription = categeoryCodeDescription;
        this.subCategoryCode = subCategoryCode;
        this.subCategoryCodeDescription = subCategoryCodeDescription;
    }
}

List<MyObject> collection = new ArrayList<MyObject>;
collection.add(new My Object("A1", "descA1", "A1A", "descA1A"));
collection.add(new My Object("A1", "descA1", "A1B", "descA1B"));
collection.add(new My Object("A1", "descA1", "A1C", "descA1C"));
collection.add(new My Object("A2", "descA1", "A2A", "descA2A"));
collection.add(new My Object("A2", "descA1", "A2B", "descA2B"));

可以按类别代码分组,但同时映射到包含描述的对象。因此,如果我有两个类。.

public class Category {
    String categoryCode;
    String categoryDesc;
    public Category (String categoryCode, String categoryDesc) {
        this.categoryCode = categoryCode;
        this.categoryDesc = categoryDesc;
    }
}

public class SubCategory {
    String subCategoryCode;
    String subCategoryDesc;
    public SubCategory (String subCategoryCode, String subCategoryDesc) {
        this.subCategoryCode = subCategoryCode;
        this.subCategoryDesc = subCategoryDesc;
    }
}

..并且我要将集合列表分组为Map<Category,List<SubCategory>>。我可以对类别代码进行分组,但是看不到如何创建新的类别实例作为映射键。在一个衬套中可能无法做到这一点。

Map<String, List<MyObject>> map = collection.stream().collect(Collectors.groupingBy(MyObject::getCategoryCode));

2 个答案:

答案 0 :(得分:4)

如果将mapping收集器链接到groupingBy,就可以完成。

您使用mapping()MyObject实例转换为SubCategory实例。

Map<Category,List<SubCategory>> map =
    collection.stream().collect(Collectors.groupingBy(mo -> new Category(mo.getCategoryCode(),mo.getCategoryDesc()),
                                                      Collectors.mapping(mo->new SubCategory(mo.getSubCategoryCode(),mo.getSubCategoryDesc()),
                                                                         Collectors.toList())));

请注意,Category必须覆盖equalshashCode才能使此分组正常工作。

答案 1 :(得分:4)

您可以通过使用合并功能的toMap收集器来完成此操作:

Map<Category, List<SubCategory>> result = collection.stream()
                .collect(toMap(e -> new Category(e.getCategoryCode(), e.getCategeoryCodeDescription()),
                        v -> new ArrayList<>(singletonList(new SubCategory(v.getSubCategoryCode(), v.getSubCategoryCodeDescription()))),
                        (l, r) -> { l.addAll(r); return l; }));

这假定您Category类已经重写了equals和hashcode方法,如下所示:

class Category {
    ...
    ... // properties + getters + constructors etc..
    ...

    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Category category = (Category) o;
        return Objects.equals(categoryCode, category.categoryCode);
    }

    @Override
    public int hashCode() {
        return Objects.hash(categoryCode);
    }

}

要获得更好的可读性,您可以将keyMappervalueMappermerge函数提取为以下不同函数:

private static List<SubCategory> valueMapper(MyObject v) {
        return new ArrayList<>(singletonList(new SubCategory(v.getSubCategoryCode(), v.getSubCategoryCodeDescription())));
}

private static List<SubCategory> merge(List<SubCategory> l, List<SubCategory> r) {
    l.addAll(r);
    return l;
}

private static Category keyMapper(MyObject e) {
     return new Category(e.getCategoryCode(), e.getCategeoryCodeDescription());
}

那么您可以做:

 Map<Category, List<SubCategory>> result = collection.stream()
                .collect(toMap(Main::keyMapper,
                        Main::valueMapper,
                        Main::merge));

Main是包含keyMappervalueMappermerge方法的类。