What is equivalent to C#'s Select clause in JAVA's streams API

北慕城南 提交于 2019-12-30 04:41:09

问题


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#.

Person class

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.

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


回答1:


Well, you can map Person instances to instances of anonymous classes, e.g. assuming

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;
    }
}

you can do

List<Person> lst = Arrays.asList(
                       new Person("Pava", "India", 1), new Person("tiwari", "USA", 2));
List<?> result = lst.stream()
    .map(p -> new Object() { String address = p.getAddress(); String name = p.getName(); })
    .collect(Collectors.toList());

but as you might note, it’s not as concise, and, more important, the declaration of the result variable can’t refer to the anonymous type, which makes the instances of the anonymous type almost unusable.

Currently, 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:

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());



回答2:


It seems that you want to transform your Person with 3 properties to a Holder that has 2 properties. And that is a simple map operation:

lst.stream().map(p -> new AbstractMap.SimpleEntry(p.address, p.name))
                          .collect(Collectors.toList());

This is collecting your entries to SimpleEntry that is just a Holder for two values. If you need more then two, you are out of luck - you will need to create your own holder(class).




回答3:


If you know which attributes to select and this does not change, I would recommend writing a small class with that subset of Person's attributes. You can then map every person to an instance of that class and collect them into a list:

Stream.of(new Person(1, "a", "aa"), new Person(2, "b", "bb"), new Person(3, "b", "bbb"),
          new Person(4, "c", "aa"), new Person(5, "b", "bbb"))
      .filter(person -> true)    // your filter criteria goes here
      .map(person -> new PersonSelect(person.getName(), person.getAddress()))
      .collect(Collectors.toList());

// result in list of PersonSelects with your name and address

If the set of desired attributes varies, you could use an array instead. It will look more similar to your C# code, but does not provide type safety:

Stream.of(new Person(1, "a", "aa"), new Person(2, "b", "bb"), new Person(3, "b", "bbb"),
          new Person(4, "c", "aa"), new Person(5, "b", "bbb"))
      .filter(person -> true)
      .map(person -> new Object[] {person.getName(), person.getAddress()})
      .collect(Collectors.toList())
      .forEach(p -> System.out.println(Arrays.asList(p)));

// output: [a, aa], [b, bb], [b, bbb], [c, aa], [b, bbb]



回答4:


If you want to create a list of new Person instances you first should provide a constructor, e.g. like this:

class Person {
  public int id;
  public String name;
  public String address;

  public Person( int pId, String pName, String pAddress ) {
    super();
    id = pId;
    name = pName;
    address = pAddress;
  }
}

Then you could use the stream:

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

lst.add(new Person(1, "Pava", "India" ));
lst.add(new Person( 2, "tiwari", "USA" ) );

//since id is an int we can't use null and thus I used -1 here    
List<Person> result = lst.stream().map(p -> new Person(-1, p.name, p.address)).collect(Collectors.toList());

If you want to filter persons then just put a filter() in between stream() and map():

List<Person> result = lst.stream().filter(p -> p.name.startsWith( "P" )).map(p -> new Person( -1, p.name, p.address )).collect(Collectors.toList());


来源:https://stackoverflow.com/questions/43973776/what-is-equivalent-to-cs-select-clause-in-javas-streams-api

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!