Generally there's no difference. NewClass::new produces less bytecode as in lambda version an auto-generated private method is created by java compiler from the lambda body while NewClass:new directly links to the constructor method handle. So using method references you may have slightly less class file size. No significant performance difference is expected though.
Another difference is method resolution procedure. It's not applicable in your particular example, but may be applicable in other code. For example, you have two constructors:
public NewClass(String a) {...}
public NewClass(String a, String b) {...}
And you have some method which accepts functional interface:
public myMethod(Function<String, NewClass> fn) {...}
Then you can call it both with lambda or functional interface:
myMethod(str -> new NewClass(str));
myMethod(NewClass::new);
But suppose that later you add a new method with the same name like this:
public myMethod(BiFunction<String, String, NewClass> fn) {...}
Then method reference call will become ambiguous and will result in compilation error as NewClass::new now matches to both constructors, while lambda is still unambiguous.
You did not say what ´mapToOther` should or should not do.
So assume:
mapToOther has the signature
Other mapToOther(MyObject obj), and transformsobjof type MyObject to a new object of typeOtherit does not keep a reference to
objsomewhere else (maybe in some cache)it does not put a reference to
objinto the newly created ´Other` objectthe implementation of the Stream classes is quite sane and takes cares not to create memory leaks
then the source objects are "garbage collectable" right after the assignment. If one of the above assumptions is violated, then the MyObject instances will continue to "live".
To answer your comment:
as the stream goes through each source object, would each of them be garbage collectable right after it completes the map but before all completed the map
This ultimately depends on the underlying implementation of Stream.of and map. The Stream documentation discourages multiple consumptions of a stream, hence there is no mandatory reason for the elements to be kept in scope right after they were processed. On the other hand, Stream.of uses varargs, so all of the instances of MyObject are first created and then put into an array internally before .of is executed. The most straightforward implementation of of and map might simply keep the elements in that array internally. So I would usually expect the elements are not garbage collectable before the assignment is completed.
As far as garbage collection goes it does what this does:
Other other = mapToOther(new MyObject());
That is, if mapToOther() leaves other holding a hard reference to your MyObject instance they will live and die together. If not the MyObject instance is eligible for garbage collection when you hit the semicolon. These things have been called nameless temporary objects or anonymous objects. Not every object gets directly associated with an identifier like a variable. But they can live forever in an object graph. That's one of the reasons circular references are such a bad thing.
If you want to iterate over a list and create a new list with "transformed" objects, you should use the map() function of stream + collect(). In the following example I find all people with the last name "l1" and each person I'm "mapping" to a new Employee instance.
public class Test {
public static void main(String[] args) {
List<Person> persons = Arrays.asList(
new Person("e1", "l1"),
new Person("e2", "l1"),
new Person("e3", "l2"),
new Person("e4", "l2")
);
List<Employee> employees = persons.stream()
.filter(p -> p.getLastName().equals("l1"))
.map(p -> new Employee(p.getName(), p.getLastName(), 1000))
.collect(Collectors.toList());
System.out.println(employees);
}
}
class Person {
private String name;
private String lastName;
public Person(String name, String lastName) {
this.name = name;
this.lastName = lastName;
}
// Getter & Setter
}
class Employee extends Person {
private double salary;
public Employee(String name, String lastName, double salary) {
super(name, lastName);
this.salary = salary;
}
// Getter & Setter
}
What you are possibly looking for is map(). You can "convert" the objects in a stream to another by mapping this way:
...
.map(userMeal -> new UserMealExceed(...))
...
yes, I suggest you try it with map() instead of forEach
List<Person> people = strings.stream()
.map(Person::new)
.collect(Collectors.toList());
Note: while Person::new does the same thing as s -> new Person(s) it is not exactly the same. The Person::new doesn't create a lambda method but the latter does. In the byte code, you will see a synthetic method which contains the implementation.
That's what map is for :
List<Person> persons = strings.stream()
.map(s -> new Person(s))
.collect(Collectors.toList());