Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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

like image 898
Pavan Tiwari Avatar asked May 15 '17 07:05

Pavan Tiwari


People also ask

Is C equivalent to F?

To convert temperatures in degrees Celsius to Fahrenheit, multiply by 1.8 (or 9/5) and add 32.

What is the equivalent temperature in C?

Celsius and Fahrenheit are two temperature scales. The Fahrenheit and Celsius scales have one point at which they intersect. They are equal at -40 °C and -40 °F.

How do you write equivalent in C?

Equal to operator: Represented as '==', the equal to operator checks whether the two given operands are equal or not. If so, it returns true. Otherwise, it returns false. For example, 5==5 will return true.

What is -= in C?

-= Subtract AND assignment operator. It subtracts the right operand from the left operand and assigns the result to the left operand. C -= A is equivalent to C = C - A.


2 Answers

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.

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

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.

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.

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

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());
like image 131
Holger Avatar answered Oct 29 '22 16:10

Holger


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

like image 35
Eugene Avatar answered Oct 29 '22 16:10

Eugene