开发者

How can I call a method on each element of a List?

开发者 https://www.devze.com 2023-03-31 13:30 出处:网络
Suppose that I have a list of cars : public class Car { private String brand; private String name; private String color;

Suppose that I have a list of cars :

public class Car {
    private String brand;
    private String name;
    private String color;

    public Car() { // ...  }

    public getName() { return name; }
    // ...
}

// Suppose that I have already init the list of car
List<Car> cars = //...
List<String> names = new ArrayList<String>();


for (Car c : cars ) {
    names.add(c.getName());
}

How can I shorten the code above ? In a nutshell, How can I开发者_StackOverflow社区 call a method on each element of a List ?

For example, in Python :

[car.name for car in cars]


Java 8 will (hopefully) have some form of lambda expression, which will make code like this more feasible. (Whether there'll be anything like list comprehensions is another matter.)

Your wish has been granted!

---EDIT---
lol as if on cue: forEach()

Definitely check this out.

For your question specifically, it becomes the folowing:

// Suppose that I have already init the list of car
List<Car> cars = //...
List<String> names = new ArrayList<String>();

// LAMBDA EXPRESSION
cars.forEach( (car) -> names.add(car.getName()) );

It's really quite incredible what they've done here. I'm excited to see this used in the future.

---EDIT---
I should have seen this sooner but I can't resist but to post about it.

The Stream functionality added in jdk8 allows for the map method.

// Suppose that I have already init the list of car
List<Car> cars = //...

// LAMBDA EXPRESSION
List<String> names = cars.stream().map( car -> car.getName() ).collect( Collectors.toList() );

Even more concise would be to use Java 8's method references (oracle doc).

List<String> names = cars.stream().map( Car::getName ).collect( Collectors.toList() );


UPDATE:

See aaiezza's answer for a Java 8 solution using a lambda expression.

Original pre-Java 8 answer:

The effect can be achieved with Guava, the Function implementation is already more verbose than what you have:

List<Car> cars = //...

Function<Car, String> carsToNames = new Function<Car, String>() {
   @Override
   public String apply(Car car) {
      return car.getName();
   }
}

List<String> names = Lists.transform(cars, carsToNames);

(Keep in mind that Lists.transform returns a view that will apply the function lazily - if you want an immediate copy, you need to copy the returned list into a new list.)

So this doesn't help you shorten your code, but it's an example of how verbose it is to achieve your desired affect in Java.

Edit: You might have a look at lambdaj, a library that seems to approach what you're looking for. I haven't tried this out myself, but the homepage shows this example:

List<Person> personInFamily = asList(new Person("Domenico"), new Person("Mario"), new Person("Irma"));
forEach(personInFamily).setLastName("Fusco");


other than getting rid of the braces and/or moving all the code to one line, which might not be a good idea, you can't.


In common-lisp there is a mapcar-function (this has nothing to do with the car-example above). A general mapcar-function in Java:

static <LIST_TYPE, ELEM_TYPE> List<LIST_TYPE> mapcar(Collection<ELEM_TYPE> list, Function<ELEM_TYPE, LIST_TYPE> function)
{
    return list.stream().map(function).collect(Collectors.toList());
}

For the car-example:

List<String> carNames = mapcar(cars, car -> car.getName());


Better stay away from Guava's Lists.transform unless you are aware what it is doing. Check out this snippet:

public class User {
    private Integer id;
    // constructor, getters & setters
}

public class UserDto {
    private Integer id;
    private boolean processed;

    public UserDto(User user) {
        this.id = user.getId();
        this.processed = false;
    }
   // getters & setters
}

// now let's do actual transformation ...
private List<UserDto> getUsers() {
    List<User> users = List.of(new User(1), new User(2));
    List<UserDto> userDtos = Lists.transform(users, UserDto::new);
    userDtos.forEach(userDto -> userDto.setProcessed(true));
    return userDtos;
}

You would probably expect processed flag to be set to true for all DTOs, yet this does not happen. While this is the consequence of their "lazy view" approach, it is still quite surprising.


If you are stuck and must still use Apache Commons then the Transformer class can be of help: http://apachecommonstipsandtricks.blogspot.de/2009/01/examples-of-functors-transformers.html

0

精彩评论

暂无评论...
验证码 换一张
取 消