什么相当于 JAVA 的流 API 中的 C# 的 Select 子句

2022-01-22 00:00:00 c# java-8 java java-stream

我想过滤 Person 类的列表,最后使用 Streams 映射到 Java 中的某个匿名类.我可以在 C# 中非常轻松地做同样的事情.

I wanted to filter list of Person class and finally map to some anonymous class in Java using Streams. I am able to do the same thing very easily in C#.

人物类

class Person
{
    public int Id { get; set; }

    public string Name { get; set; }

    public string Address { get; set; }
}

将结果映射为期望格式的代码.

Code to map the result in desire format.

 List<Person> lst = new List<Person>();

 lst.Add(new Person() { Name = "Pava", Address = "India", Id = 1 });
 lst.Add(new Person() { Name = "tiwari", Address = "USA", Id = 2 });
 var result = lst.Select(p => new { Address = p.Address, Name = p.Name }).ToList();

现在,如果我想访问任何新创建类型的属性,我可以使用下面提到的语法轻松访问.

Now if I wanted to access any property of newly created type I can easily access by using below mentioned syntax.

Console.WriteLine( result[0].Address);

理想情况下,我应该使用循环来遍历结果.

Ideally I should use loop to iterate over the result.

我知道在 java 中我们为 ToList 收集和为 Select 映射.但我无法只选择 Person 类的两个属性.我该怎么做Java

I know that in java we have collect for ToList and map for Select. But i am unable to select only two property of Person class. How can i do it Java

推荐答案

Java 没有结构类型.您可以将值映射到的最接近的是匿名类的实例.但也有明显的缺点.从 Java 16 开始,使用 record 会是更好的解决方案,即使它是命名类型并且可能稍微冗长一些.

Java does not have structural types. The closest you could map the values to, are instances of anonymous classes. But there are significant drawbacks. Starting with Java 16, using record would be the better solution, even if it’s a named type and might be slightly more verbose.

例如假设

class Person {
    int id;
    String name, address;

    public Person(String name, String address, int id) {
        this.id = id;
        this.name = name;
        this.address = address;
    }
    public int getId() {
        return id;
    }
    public String getName() {
        return name;
    }
    public String getAddress() {
        return address;
    }
}

你可以的

List<Person> lst = List.of(
    new Person("Pava", "India", 1), new Person("tiwari", "USA", 2));
var result = lst.stream()
    .map(p -> {
        record NameAndAddress(String name, String address){}
        return new NameAndAddress(p.getName(), p.getAddress());
    })
    .collect(Collectors.toList());
result.forEach(x -> System.out.println(x.name() + " " + x.address()));

匿名内部类替代方案看起来像

The anonymous inner class alternative would look like

List<Person> lst = List.of(
    new Person("Pava", "India", 1), new Person("tiwari", "USA", 2));
var result = lst.stream()
    .map(p -> new Object(){ String address = p.getAddress(); String name = p.getName();})
    .collect(Collectors.toList());
result.forEach(x -> System.out.println(x.name + " " + x.address));

但正如您可能注意到的那样,它仍然不如结构类型简洁.使用 var 声明 result 变量是引用我们无法通过名称引用的类型的唯一方法.这需要 Java 10 或更高版本,并且仅限于方法的范围.

but as you might note, it’s still not as concise as a structural type. Declaring the result variable using var is the only way to refer to the type we can not refer to by name. This requires Java 10 or newer and is limited to the method’s scope.

同样重要的是要记住,内部类可能会由于捕获对周围 this 的引用而造成内存泄漏.在示例中,每个对象还捕获用于其初始化的 p 的值.record 没有这些问题,而且它会自动获得合适的 equalshashCodetoString 实现,这意味着打印 System.out.println(result); 之类的列表或将其传输到 new HashSet<>(result) 之类的集合将产生有意义的结果.

It’s also important to keep in mind that inner classes can create memory leaks due to capturing a reference to the surrounding this. In the example, each object also captures the value of p used for its initialization. The record doesn’t have these problems and further, it automatically gets suitable equals, hashCode, and toString implementations, which implies that printing the list like System.out.println(result); or transferring it to a set like new HashSet<>(result) will have meaningful results.

此外,将 record 的声明移动到更广泛的范围要容易得多.

Also, it’s much easier to move the record’s declaration to a broader scope.

在 Java 10 之前,lambda 表达式是唯一支持声明隐含类型变量的 Java 功能,该隐含类型可以是匿名的.例如,即使在 Java 8 中也可以使用以下内容:

Prior to Java 10, lambda expressions are the only Java feature that supports declaring variables of an implied type, which could be anonymous. E.g., the following would work even in Java 8:

List<String> result = lst.stream()
    .map(p -> new Object(){ String address = p.getAddress(); String name = p.getName();})
    .filter(anon -> anon.name.startsWith("ti"))
    .map(anon -> anon.address)
    .collect(Collectors.toList());

相关文章