简体   繁体   English

Java:获取class成员列表,用于多级嵌套列表中的列表

[英]Java: Obtain class member list, for a list within multilevel nested list

In Java, how do I map and obtain a class member list, using a list within list.在Java中,我如何使用列表中的列表来获取class成员列表map。

public class CustomerSales {
    public List<Product> productList;
    ....
}

public class Product {
    public List<ProductSubItem> productSubItemList
    ....
}

public class ProductSubItem {
    public String itemName;

Attempt:试图:

However, this does not get the itemName.但是,这不会获取 itemName。 looking for a clean efficient method, ideally may want to attempt 4-5 levels deep, however question has only 3 for simplicity, etc寻找一种干净有效的方法,理想情况下可能想尝试深入 4-5 层,但是为了简单起见,问题只有 3 层,等等

List<String> itemNameList = customerSales.productList.stream().map(p -> p.productSubItemList()).collect(Collectors.toList()); 

using Java 8使用 Java 8

Attempted using this resource: still not luck, How can I get a List from some class properties with Java 8 Stream?尝试使用此资源:仍然不走运, 如何从 class 属性中获取 Java 8 Stream 的列表?

Convert sub-list to a stream and use flatMap to convert stream of streams of elements to stream of elements.将子列表转换为 stream 并使用 flatMap 将 stream 元素流转换为 stream 元素流。

Example:例子:

package x.mvmn.demo;

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

public class Demo {

    public static class CustomerSales {
        public List<Product> productList;
    }

    public static class Product {
        public List<ProductSubItem> productSubItemList;

        public List<ProductSubItem> getProductSubItemList() {
            return productSubItemList;
        }
    }

    public static class ProductSubItem {
        public String itemName;

        public ProductSubItem(String itemName) {
            this.itemName = itemName;
        }

        public String getItemName() {
            return itemName;
        }
    }

    public static void main(String args[]) throws Exception {
        // Setup mock data
        CustomerSales customerSales = new CustomerSales();
        Product p1 = new Product();
        p1.productSubItemList = Arrays.asList(new ProductSubItem("p1 item one"), new ProductSubItem("p1 item two"));
        Product p2 = new Product();
        p2.productSubItemList = Arrays.asList(new ProductSubItem("p2 item one"), new ProductSubItem("p2 item two"));
        customerSales.productList = Arrays.asList(p1, p2);

        // Get list of item names
        System.out.println(customerSales.productList.stream().map(Product::getProductSubItemList).flatMap(List::stream)
                .map(ProductSubItem::getItemName).collect(Collectors.toList()));
        // Alternative syntax
        System.out.println(customerSales.productList.stream().flatMap(product -> product.productSubItemList.stream())
                .map(subItem -> subItem.itemName).collect(Collectors.toList()));
    }
}

Output: Output:

[p1 item one, p1 item two, p2 item one, p2 item two]
[p1 item one, p1 item two, p2 item one, p2 item two]

Looks like you need to use flatMap:看起来你需要使用 flatMap:

https://www.baeldung.com/java-difference-map-and-flatmap https://www.baeldung.com/java-difference-map-and-flatmap

List<String> itemNameList = customerSales.productList.stream().map(p -> p.productSubItemList().stream()).collect(Collectors.toList()); 

Here is another example of flattening list of lists https://www.baeldung.com/java-flatten-nested-collections这是列表的扁平化列表的另一个示例https://www.baeldung.com/java-flatten-nested-collections

public <T> List<T> flattenListOfListsStream(List<List<T>> list) {
    return list.stream()
      .flatMap(Collection::stream)
      .collect(Collectors.toList());    
}

We can re-write the @mvmn answer using below.我们可以使用下面的方法重写@mvmn 的答案。

public class Demo {

    public static class CustomerSales {
        public List<Product> productList;

        public Stream<Product> getProductStream() {
            return !CollectionUtils.isEmpty(productList) ? productList.stream() : Stream.empty();
        }
    }

    public static class Product {
        public List<ProductSubItem> productSubItemList;

        public List<ProductSubItem> getProductSubItemList() {
            return productSubItemList;
        }

        public Stream<ProductSubItem> getProductSubItemStream() {
            return !CollectionUtils.isEmpty(productSubItemList) ? productSubItemList.stream() : Stream.empty();
        }
    }

    public static class ProductSubItem {
        public String itemName;

        public ProductSubItem(String itemName) {
            this.itemName = itemName;
        }

        public String getItemName() {
            return itemName;
        }
    }

    public static void main(String args[]) throws Exception {
        // Setup mock data
        CustomerSales customerSales = new CustomerSales();
        Product p1 = new Product();
        p1.productSubItemList = Arrays.asList(new ProductSubItem("p1 item one"), new ProductSubItem("p1 item two"));
        Product p2 = new Product();
        p2.productSubItemList = Arrays.asList(new ProductSubItem("p2 item one"), new ProductSubItem("p2 item two"));
        customerSales.productList = Arrays.asList(p1, p2);

        // With Utility method in our DTOs
        System.out.println(customerSales.getProductStream()
                                        .flatMap(Product::getProductSubItemStream)
                                        .map(ProductSubItem::getItemName)
                                        .collect(Collectors.toList()));

        // Java16+
        System.out.println(customerSales.getProductStream()
                                        .flatMap(Product::getProductSubItemStream)
                                        .map(ProductSubItem::getItemName)
                                        .toList());
    }
}

In case when you need to perform a one-to-one transformation - use map() operation.如果您需要执行一对一转换 - 使用map()操作。 For one-to-many transformations, use flatMap() operation.对于一对多转换,使用flatMap()操作。

flatMap() expects a Function which consumes a stream and produces a Stream of resulting type. flatMap()期望Function消耗 stream 并生成结果类型的 Stream。

Java 16 offers one more alternative for one-to-many transformations, namely mapMulti() , which would be more beneficial in terms of performance ( because it doesn't require generating a new Stream internally like flatMap does ) if the associated collection is expected to contain of a few elements or even might be empty. Java 16 为一对多转换提供了另一种选择,即mapMulti() ,这在性能方面会更有利(因为它不需要像flatMap那样在内部生成一个新的 Stream )如果相关集合是预期的包含一些元素甚至可能是空的。 If you can't predict the number of elements in the resulting stream, then flatMap() is the way to go.如果您无法预测结果 stream 中的元素数量,那么flatMap()是通往 go 的途径。

However, this does not get the itemName.但是,这不会获取 itemName。 looking for a clean efficient method, ideally may want to attempt 4-5 levels deep寻找一种干净高效的方法,理想情况下可能想尝试4-5层深

To tackle the scenario, you've described, you can introduce a uniform method producing a Function which turns a stream element into another stream (a Function required by the flatMap ), and utilize this method in the Stream for the purpose of conciseness.为了解决您描述的场景,您可以引入一种生成Function的统一方法,该方法将 stream 元素转换为另一个 stream( flatMap所需的 Function),并在 Stream 中使用此方法以达到简洁的目的。

public static <T, R> Function<T, Stream<R>> flatten(
    Function<T, ? extends Collection<R>> mapper
) {
    return t -> mapper.apply(t).stream();
}

Now let's consider the following object-graph with a diversity of nested collections:现在让我们考虑以下具有嵌套 collections 多样性的对象图:

ClassA -> List<ClassB>
ClassB -> Set<ClassC>
ClassC -> Map<Integer, ClassD> mapD
ClassD -> Queue<ClassE> queueE
ClassE -> String name

If we have a List of ClassA instances, the List name from ClassE can be obtained in the following way:如果我们有一个ClassA实例列表,可以通过以下方式从ClassE中获取列表名称:

List<String> names = aList.stream()              // Stream<ClassA>
    .flatMap(flatten(ClassA::getListB))          // Stream<ClassB>
    .flatMap(flatten(ClassB::getSetC))           // Stream<ClassC>
    .flatMap(flatten(c -> c.getMapD().values())) // Stream<ClassD>
    .flatMap(flatten(ClassD::getQueueE))         // Stream<ClassE>
    .map(ClassE::getName)                        // Stream<String>
    .toList(); // for Java 16 or collect(Collectors.toList()

Here's a complete example.这是一个完整的例子。

Dummy classes:虚拟类:

public class ClassA {
    private List<ClassB> listB;
    
    // getters, all-args constructor
}

public class ClassB {
    private Set<ClassC> setC;

    // getters, all-args constructor
}

public class ClassC {
    private Map<Integer, ClassD> mapD;

    // getters, all-args constructor
}

public class ClassD {
    private Queue<ClassE> queueE;

    // getters, all-args constructor
}

public class ClassE {
    private String name;

    // getters, all-args constructor
}

main()

public static void main(String[] args) {
    List<ClassA> aList = List.of(new ClassA(
        List.of(
            new ClassB(
                Set.of(
                    new ClassC(Map.of(
                        1, new ClassD(new ArrayDeque<>(List.of(new ClassE("Alice")))),
                        2, new ClassD(new ArrayDeque<>(List.of(new ClassE("Bob"))))
                    )),
                    new ClassC(Map.of(1, new ClassD(new ArrayDeque<>(List.of(new ClassE("Carol"))))))
                )
            )
        )
    ));
    
    List<String> names = aList.stream()
        .flatMap(flatten(ClassA::getListB))
        .flatMap(flatten(ClassB::getSetC))
        .flatMap(flatten(c -> c.getMapD().values()))
        .flatMap(flatten(ClassD::getQueueE))
        .map(ClassE::getName)
        .toList(); // for Java 16 or collect(Collectors.toList()

    System.out.println(names);
}

Output: Output:

[Alice, Bob, Carol]

A link to Online Demo在线演示链接

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM