如何将ArrayList中的元素分组并分成三个List

How to group element in ArrayList and divide into three List

我有一个实体class

    class Entity {
        private String customer;
        private String product;
        private String productDetail;
    }

我有一个 ArrayList<Entity> 包括很多记录,例如我列表中的记录:

customer    product    productDetail
   A          A1            A11
   A          A1            A12
   A          A2            A21
   A          A2            A22
   B          B1            B11
   B          B2            B21
   C          C1            C11
   C          C1            C12

我下面有 3 个实体

   class ProductDetail{
       private String details;
   }

   class Product{
       private String product;
       private List<ProductDetail> detailList;
   }

   class Customer{
       private String customer;
       private List<Product> productList;
   }

我想遍历 ArrayList<Entity> 并将记录分组到 Customer class、Customer class 包括 productListproductList 包括 detailList.

请给我一个解决方案。

更新我的代码:

客户实体:

public class CustomerEntity {
    private int customer;

    private List<ProductEntity> productList;

    public int getCustomer() {
        return customer;
    }

    public void setCustomer(int customer) {
        this.customer = customer;
    }

    public List<ProductEntity> getProductList() {
        return productList;
    }

    public void setProductList(List<ProductEntity> productList) {
        this.productList = productList;
    }

}

产品实体:

public class ProductEntity {

    private int product;
    private List<ProductDetailEntity> detailList;

    public int getProduct() {
        return product;
    }

    public void setProduct(int product) {
        this.product = product;
    }

    public List<ProductDetailEntity> getDetailList() {
        return detailList;
    }

    public void setDetailList(List<ProductDetailEntity> detailList) {
        this.detailList = detailList;
    }

}

产品详细实体:

public class ProductDetailEntity {
    private int details;

    public int getDetails() {
        return details;
    }

    public void setDetails(int details) {
        this.details = details;
    }

}

我的虚拟代码测试:

public class TestList {
    public static void main(String[] args) {
        TestEntity testEntity;
        List<TestEntity> list = new ArrayList<TestEntity>();

        // Create Dummy Data
        testEntity = new TestEntity();
        testEntity.setCustomer("A");
        testEntity.setProduct("A1");
        testEntity.setProductDetail("A11");
        list.add(testEntity);

        testEntity = new TestEntity();
        testEntity.setCustomer("A");
        testEntity.setProduct("A1");
        testEntity.setProductDetail("A12");
        list.add(testEntity);

        testEntity = new TestEntity();
        testEntity.setCustomer("A");
        testEntity.setProduct("A2");
        testEntity.setProductDetail("A21");
        list.add(testEntity);

        testEntity = new TestEntity();
        testEntity.setCustomer("A");
        testEntity.setProduct("A2");
        testEntity.setProductDetail("A22");
        list.add(testEntity);

        testEntity = new TestEntity();
        testEntity.setCustomer("B");
        testEntity.setProduct("B1");
        testEntity.setProductDetail("B11");
        list.add(testEntity);

        testEntity = new TestEntity();
        testEntity.setCustomer("B");
        testEntity.setProduct("B2");
        testEntity.setProductDetail("B21");
        list.add(testEntity);

        testEntity = new TestEntity();
        testEntity.setCustomer("C");
        testEntity.setProduct("C1");
        testEntity.setProductDetail("C11");
        list.add(testEntity);

        testEntity = new TestEntity();
        testEntity.setCustomer("C");
        testEntity.setProduct("C1");
        testEntity.setProductDetail("C12");
        list.add(testEntity);

        Map<String, List<TestEntity>> groupByCustomerMap = new LinkedHashMap<String, List<TestEntity>>();
        List<TestEntity> tempEntityList;
        TestEntity tempEntity;

        // Group record by customer
        for (TestEntity item : list) {
            String customer = item.getCustomer();

            tempEntityList = new ArrayList<TestEntity>();
            tempEntity = new TestEntity();

            tempEntity.setCustomer(customer);
            tempEntity.setProductDetail(item.getProductDetail());
            tempEntity.setProduct(item.getProduct());

            tempEntityList.add(tempEntity);

            if (!groupByCustomerMap.containsKey(customer)) {
                groupByCustomerMap.put(customer, tempEntityList);
            } else {
                groupByCustomerMap.get(customer).addAll(tempEntityList);
            }
        }

        // I think from groupByCustomerMap, read ProductDetail and group by product
        // Pleaes suggest me next solution
    }
}

我会尝试的典型方法如下:

  • 创建一个空的客户列表
  • 创建一个空的产品列表
  • 遍历包含 Entity 个元素的输入数组列表。
    • 如果当前实体中包含的客户不在客户列表中,则创建并添加该客户。然后检查产​​品。如果创建了客户,则 productList 应该为空,需要添加产品。如果客户已经存在,请检查产品列表中是否存在该产品....
    • 对于填充产品列表,您可以选择类似的方法。基本检查是一样的。

我可以提出下一个决定:

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.ToString;
import org.junit.Test;

import java.util.ArrayList;
import java.util.List;

/**
 * Created by smv on 19/09/16.
 */
public class MainTest {

    @AllArgsConstructor
    @ToString
    @Data
    class Entity {
        private String customer;
        private String product;
        private String productDetail;
    }

    @AllArgsConstructor
    @ToString
    @Data
    class ProductDetail{
        private String details;
    }

    @AllArgsConstructor
    @ToString
    @Data
    class Product{
        private String product;
        private List<ProductDetail> detailList;
    }

    @AllArgsConstructor
    @ToString
    @Data
    class Customer{
        private String customer;
        private List<Product> productList;
    }
    @Test
    public void run() throws Exception {
        ArrayList<Entity> entities = new ArrayList<>();
        entities.add(new Entity("A", "A1", "A11"));
        entities.add(new Entity("A", "A1", "A12"));
        entities.add(new Entity("A", "A2", "A21"));
        entities.add(new Entity("A", "A2", "A22"));
        entities.add(new Entity("B", "B1", "B11"));
        entities.add(new Entity("B", "B2", "B21"));
        entities.add(new Entity("C", "C1", "C11"));
        entities.add(new Entity("C", "C1", "C12"));

        ArrayList<Customer> customers = new ArrayList<>();
        entities.forEach(entity -> {
            Customer customer = customers.stream().filter(c -> c.getCustomer().equals(entity.getCustomer())).findFirst().orElse(new Customer(entity.getCustomer(), new ArrayList<>()));
            if (!customers.contains(customer)) {
                customers.add(customer);
            }
            Product product = customer.getProductList().stream().filter(p -> p.getProduct().equals(entity.getProduct())).findFirst().orElse(new Product(entity.getProduct(), new ArrayList<>()));
            if (!customer.getProductList().contains(product)) {
                customer.getProductList().add(product);
            }
            ProductDetail productDetail = product.getDetailList().stream().filter(pd -> pd.getDetails().equals(entity.getProductDetail())).findFirst().orElse(new ProductDetail(entity.getProductDetail()));
            if (!product.getDetailList().contains(productDetail)) {
                product.getDetailList().add(productDetail);
            }
        });

        customers.forEach(s -> System.out.println(s));
    }

}

我认为这不是最好的变体,但它确实有效。我认为必须有更优雅的方法来替换代码中的 if

UPD

数据bean 上的注释Lombok lib 注释提供了声明bean 的便捷方式。它也适用于 java 7。

@AllArgsConstructor - adds the constructor with all fields.
@Data - generate getters/setters for all fields.
@ToString - add the method that returns the string representation of the bean.

更多语法仅适用于 java 8。这是带有 lambda 表达式的函数 List.forEach,显然,它迭代列表并且可以用 for 循环替换。 使用 Java 8 流 API(filter 带谓词表达式)和可选类型 (orElse) 找到或创建每个数据 bean。 所以这一行:

Customer customer = customers.stream().filter(c -> c.getCustomer().equals(entity.getCustomer())).findFirst().orElse(new Customer(entity.getCustomer(), new ArrayList<>()));

可以在 java 7 中替换为:

Customer customer = null;
for (Customer c: customers ) {
        if(c.getCustomer().equals(entity.getCustomer())) {
            customer = c;
            break;
        }
    }
if (customer == null) {
  customer  = new Customer(entity.getCustomer(), new ArrayList<>())
}

等等..

这是我将您的任务作为我自己的实现 CustomerList class。

class 的 API 是 ctor CustomerList(List<Entity> data) 和真正工作的主要方法 List<Customer> getCustomers()

实现基于Java8Collector构想。它在内部使用了 3 个专用函数:ACCUMULATORCOMBINERFINISHER.

整个实现思路是将输入 List<Entity> 转换为树型数据结构 Map<String, Map<String, Set<String>>。外部映射表示客户名称到产品的映射。内部映射表示产品名称到产品详细信息的映射。 Set<String> 表示一组产品详细信息名称。 ACCUMULATOR 用于进行此转换。

FINISHER用于将这个内部的树型结构转化为输出List<Customer>数据。

收集器使用COMBINER合并多个内部树状结构进行并发处理。我没有对此进行测试,但我认为收集器可以通过简单的 data.parallelStream().collect(COLLECTOR) 语句更改来完成此处理。

public class CustomerList {

    private static final BiConsumer<Map<String, Map<String, Set<String>>>, Entity> ACCUMULATOR =
            (m, e) ->
                    m.computeIfAbsent(e.getCustomer(), k -> new HashMap<>())
                            .computeIfAbsent(e.getProduct(), l -> new HashSet<>())
                            .add(e.getProductDetail());

    private static final BinaryOperator<Map<String, Map<String, Set<String>>>> COMBINER =
            (m1, m2) -> {
                Map<String, Map<String, Set<String>>> r = new HashMap<>(m1);
                for (Map.Entry<String, Map<String, Set<String>>> e : m2.entrySet())
                    r.merge(e.getKey(), e.getValue(), CustomerList::mergeProducts);
                return r;
            };

    private static final Function<Map<String, Map<String, Set<String>>>, List<Customer>> FINISHER =
            CustomerList::createCustomerList;

    private static final Collector<Entity, Map<String, Map<String, Set<String>>>, List<Customer>> COLLECTOR =
            Collector.of(HashMap::new, ACCUMULATOR, COMBINER, FINISHER);

    private final List<Entity> data;

    public CustomerList(List<Entity> data) {
        this.data = data;
    }

    public List<Customer> getCustomers() {
        return data.stream().collect(COLLECTOR);
    }

    private static Map<String, Set<String>> mergeProducts(Map<String, Set<String>> m1, Map<String, Set<String>> m2) {
        Map<String, Set<String>> r = new HashMap<>(m1);
        for (Map.Entry<String, Set<String>> e : m2.entrySet())
            r.merge(e.getKey(), e.getValue(), CustomerList::mergeDescriptions);
        return r;
    }

    private static Set<String> mergeDescriptions(Set<String> s1, Set<String> s2) {
        Set<String> r = new HashSet<>(s1);
        r.addAll(s2);
        return r;
    }

    private static List<Customer> createCustomerList(Map<String, Map<String, Set<String>>> customers) {
        return customers.entrySet().stream()
                .map(e -> new Customer(e.getKey(), createProductList(e.getValue())))
                .collect(Collectors.toList());
    }

    private static List<Product> createProductList(Map<String, Set<String>> products) {
        return products.entrySet().stream()
                .map(e -> new Product(e.getKey(), createDetailsList(e.getValue())))
                .collect(Collectors.toList());
    }

    private static List<ProductDetail> createDetailsList(Set<String> details) {
        return details.stream()
                .map(e -> new ProductDetail(e))
                .collect(Collectors.toList());
    }
}

演示

我对这个问题很感兴趣,所以我花了几个小时想出了以下问题:

List<Entity> input = asList(
        new Entity("A", "A1", "A11"),
        new Entity("A", "A1", "A12"),
        new Entity("A", "A2", "A21"),
        new Entity("A", "A2", "A22"),
        new Entity("B", "B1", "B11"),
        new Entity("B", "B2", "B21"),
        new Entity("C", "C1", "C11"),
        new Entity("C", "C1", "C12"));

List<Customer> output = input.stream().collect(
    toTree(Entity::getCustomer, Customer::new, toList(),
        toTree(Entity::getProduct, Product::new, toList(),
            mapping(Entity::getProductDetail, mapping(ProductDetail::new, toList())))));

System.out.println(output);

实施

toTree 的实现如下所示:

<E, K, C, P, R> Collector<E, ?, R> toTree(Function<E, K> keyMapper,
                                          BiFunction<K, C, P> keyWithChildrenMerger,
                                          Collector<P, ?, R> parentsCollector,
                                          Collector<E, ?, C> childrenCollector) {
    return collectingAndThen(
        groupingBy(keyMapper, LinkedHashMap::new, childrenCollector),
        keysWithChildren -> keysWithChildren.entrySet().stream()
                .map(entryFunction(keyWithChildrenMerger))
                .collect(parentsCollector));
}

<K, V, R> Function<Map.Entry<K, V>, R> entryFunction(BiFunction<K, V, R> keyValueFunction) {
    return entry -> keyValueFunction.apply(entry.getKey(), entry.getValue());
}

它是一个 Collector,接受输入 E(在我们的例子中是实体),将它们分组在一个键 K 上,收集它们的 children C ,将键 K 及其 children C 合并到 parents P,并将它们收集到某个结果 R(在我们的例子中,List<P>).

解释

我们在这里做的是将实体收集到一个 ListCustomer 树中,每个树都包含 ListProduct 的树,每个包含 ListProductDetails.

自下而上构建树

它通过在 Entity::getCustomer 上对客户进行分组,在 Entity::getProduct 上对产品进行分组,并将实体映射到 ProductDetail,然后通过调用自下而上创建树构造函数 Product::new 及其所有 ProductDetail,构造函数 Customer::new 及其所有 Products.

模特

模型 类 如您所料:

class Entity {
    private final String customer;
    private final String product;
    private final String productDetail;

    public Entity(String customer, String product, String productDetail) {
        this.customer = customer;
        this.product = product;
        this.productDetail = productDetail;
    }

    public String getCustomer() {
        return customer;
    }

    public String getProduct() {
        return product;
    }

    public String getProductDetail() {
        return productDetail;
    }
}

class ProductDetail {
    private String details;

    public ProductDetail(String details) {
        this.details = details;
    }

    @Override
    public String toString() {
        return details;
    }
}

class Product {
    private String product;
    private List<ProductDetail> details;

    public Product(String product, List<ProductDetail> productDetails) {
        this.product = product;
        details = productDetails;
    }

    @Override
    public String toString() {
        return "Product{" + product +
                ", " + details +
                '}';
    }
}

class Customer {
    private String customer;
    private List<Product> products;

    public Customer(String customer, List<Product> products) {
        this.customer = customer;
        this.products = products;
    }

    @Override
    public String toString() {
        return "Customer{" + customer +
                ", " + products +
                '}';
    }
}