拆分 Java 流中的对象
Splitting objects inside Java stream
我想知道是否可以在 Stream 中拆分对象。例如,对于这个 Employee
:
public class Employee {
String name;
int age;
double salary;
public Employee(String name, int age, double salary) {
this.name = name;
this.age = age;
this.salary = salary;
}
public String getName() { return name; }
public int getAge() { return age; }
public double getSalary() { return salary; }
}
我想在流中执行一些操作。为简单起见,让它像这样(假设我的代码架构不允许将其放入 Employee class - 否则它会太容易):
public void someOperationWithEmployee(String name, int age, double salary) {
System.out.format("%s %d %.0f\n", name, age, salary);
}
现在看起来像这样:
Stream.of(new Employee("Adam", 38, 3000), new Employee("John", 19, 2000))
// some conversations go here ...
.forEach(e -> someOperationWithEmployee(e.getName, e.getAge(), e.getSalary));
问题是,是否可以像这样将一些代码放入流中?
Stream.of(new Employee("Adam", 38, 3000), new Employee("John", 19, 2000))
// some conversations go here
.forEach((a, b, c) -> someOperationWithEmployee(a, b, c));
我想达到什么目的? - 我想如果我可以映射一些对象字段然后像处理它们一样 .forEach(this::someOperationWithEmployee)
代码可读性会稍微提高。
2015 年 5 月 14 日更新
毫无疑问,将 Employee
对象传递给 someOperationWithEmployee
是这种情况下最好的解决方案,但有时我们在现实生活中无法做到这一点,应该是使用 lambda 的通用解决方案。
简短的回答是否定的,你不能这样做。我能想到的最短的解决方案是像这样定义你自己的功能接口:
import java.util.function.Function;
@FunctionalInterface
public interface TriFunction<A,B,C,R> {
R apply(A a, B b, C c);
static <I,A,B,C,R> Function<I,R> convert(TriFunction<A,B,C,R> triFn, Function<I,A> aFn,
Function<I,B> bFn, Function<I,C> cFn) {
return i -> triFn.apply(aFn.apply(i), bFn.apply(i), cFn.apply(i));
}
}
并像这样使用它:
Stream.of(new Employee("Adam", 38, 3000), new Employee("John", 19, 2000))
// some conversations go here
.forEach(TriFunction.convert((a, b, c) -> someOperationWithEmployee(a, b, c),
Employee::getName, Employee::getAge, Employee::getSalary));
虽远非美
我认为如果您的 someOperationWithEmployee
将 Employee
对象作为参数会更好。
更新:对于对值,您可以像这样使用我的免费StreamEx库:
StreamEx.of(new Employee("Adam", 38, 3000), new Employee("John", 19, 2000))
// some conversations go here
.mapToEntry(Employee::getName, Employee::getAge)
.forKeyValue((a, b) -> someOperationWithEmployee(a, b));
但是它仅限于成对,所以你不能用这种方式处理三个或更多的值(我不会添加这样的函数)。
我也检查了jOOL library as it's concentrated on tuples and already provides interfaces like Function3
。但是,似乎也没有简单的方法可以使用它来解决您的问题。
我不确定这是否符合您的需求,但它可以进行一些调整而不检查某些类型。
你可以这样运行我的解决方案:
Stream.of(new Employee("Adam", 38, 3000), new Employee("John", 19, 2000))
.forEach(
e->ArrayCaller.<TriConsumer<String, Integer, Double>>convert(e::getName, e::getAge, e::getSalary)
.call((a, b, c) -> operation(a, b, c)));
它将调用 'main' class:
的这个简单方法
private void operation(String name, int age, double salary) {
System.out.format("%s %d %.0f\n", name, age, salary);
}
当然需要这样的辅助类型:
/** Extending interfaces must have a method called consume with N args */
interface NConsumer {}
/*
* Method must be called consume for reflection.
*
* You can define N interfaces like this.
*/
nterface TriConsumer<A, B, C> extends NConsumer {
void consume(A a, B b, C c);
}
interface ArrayCaller<E extends NConsumer> {
void call(E code);
static <T extends NConsumer> ArrayCaller<T> convert(Supplier<?>...argSuppliers) {
final Object[] args = new Object[argSuppliers.length];
for (int i = 0; i < argSuppliers.length; i++) {
args[i] = argSuppliers[i].get();
}
return new ArrayCaller<T>() {
@Override
public void call(T code) {
for (Method m: code.getClass().getMethods()) {
if (m.getName().equals("consume")) {
try {
m.invoke(code, args);
} catch (IllegalAccessException
| IllegalArgumentException
| InvocationTargetException e) {
throw new RuntimeException(e);
}
}
}
}
};
}
}
这里按照自己的方式重新设计了user270349的答案,没有反思。通过静态导入,它可以像这样使用:
Stream.of(new Employee("Adam", 38, 3000), new Employee("John", 19, 2000))
.map(e -> retrieve(e::getName, e::getAge, e::getSalary))
.forEach(c -> c.call(this::printNameAgeSalary));
或者像这样:
Stream.of(new Employee("Adam", 38, 3000), new Employee("John", 19, 2000))
.forEach(e -> retrieve(e::getName, e::getAge).call(this::printNameAge));
代码如下:
private void printNameAgeSalary(String name, int age, double salary) {
System.out.format("%s %d %.0f\n", name, age, salary);
}
private void printNameAge(String name, int age) {
System.out.format("%s %d\n", name, age);
}
public interface Consumer2<T1, T2> {
void consume(T1 t1, T2 t2);
}
public interface Consumer3<T1, T2, T3> {
void consume(T1 t1, T2 t2, T3 t3);
}
public interface Caller<E> {
void call(E code);
static <S1, S2, S3> Caller<Consumer3<S1, S2, S3>> retrieve(Supplier<S1> s1, Supplier<S2> s2, Supplier<S3> s3) {
return (Consumer3<S1, S2, S3> code) -> code.consume(s1.get(), s2.get(), s3.get());
}
static <S1, S2> Caller<Consumer2<S1, S2>> retrieve(Supplier<S1> s1, Supplier<S2> s2) {
return (Consumer2<S1, S2> code) -> code.consume(s1.get(), s2.get());
}
}
我想知道是否可以在 Stream 中拆分对象。例如,对于这个 Employee
:
public class Employee {
String name;
int age;
double salary;
public Employee(String name, int age, double salary) {
this.name = name;
this.age = age;
this.salary = salary;
}
public String getName() { return name; }
public int getAge() { return age; }
public double getSalary() { return salary; }
}
我想在流中执行一些操作。为简单起见,让它像这样(假设我的代码架构不允许将其放入 Employee class - 否则它会太容易):
public void someOperationWithEmployee(String name, int age, double salary) {
System.out.format("%s %d %.0f\n", name, age, salary);
}
现在看起来像这样:
Stream.of(new Employee("Adam", 38, 3000), new Employee("John", 19, 2000))
// some conversations go here ...
.forEach(e -> someOperationWithEmployee(e.getName, e.getAge(), e.getSalary));
问题是,是否可以像这样将一些代码放入流中?
Stream.of(new Employee("Adam", 38, 3000), new Employee("John", 19, 2000))
// some conversations go here
.forEach((a, b, c) -> someOperationWithEmployee(a, b, c));
我想达到什么目的? - 我想如果我可以映射一些对象字段然后像处理它们一样 .forEach(this::someOperationWithEmployee)
代码可读性会稍微提高。
2015 年 5 月 14 日更新
毫无疑问,将 Employee
对象传递给 someOperationWithEmployee
是这种情况下最好的解决方案,但有时我们在现实生活中无法做到这一点,应该是使用 lambda 的通用解决方案。
简短的回答是否定的,你不能这样做。我能想到的最短的解决方案是像这样定义你自己的功能接口:
import java.util.function.Function;
@FunctionalInterface
public interface TriFunction<A,B,C,R> {
R apply(A a, B b, C c);
static <I,A,B,C,R> Function<I,R> convert(TriFunction<A,B,C,R> triFn, Function<I,A> aFn,
Function<I,B> bFn, Function<I,C> cFn) {
return i -> triFn.apply(aFn.apply(i), bFn.apply(i), cFn.apply(i));
}
}
并像这样使用它:
Stream.of(new Employee("Adam", 38, 3000), new Employee("John", 19, 2000))
// some conversations go here
.forEach(TriFunction.convert((a, b, c) -> someOperationWithEmployee(a, b, c),
Employee::getName, Employee::getAge, Employee::getSalary));
虽远非美
我认为如果您的 someOperationWithEmployee
将 Employee
对象作为参数会更好。
更新:对于对值,您可以像这样使用我的免费StreamEx库:
StreamEx.of(new Employee("Adam", 38, 3000), new Employee("John", 19, 2000))
// some conversations go here
.mapToEntry(Employee::getName, Employee::getAge)
.forKeyValue((a, b) -> someOperationWithEmployee(a, b));
但是它仅限于成对,所以你不能用这种方式处理三个或更多的值(我不会添加这样的函数)。
我也检查了jOOL library as it's concentrated on tuples and already provides interfaces like Function3
。但是,似乎也没有简单的方法可以使用它来解决您的问题。
我不确定这是否符合您的需求,但它可以进行一些调整而不检查某些类型。
你可以这样运行我的解决方案:
Stream.of(new Employee("Adam", 38, 3000), new Employee("John", 19, 2000))
.forEach(
e->ArrayCaller.<TriConsumer<String, Integer, Double>>convert(e::getName, e::getAge, e::getSalary)
.call((a, b, c) -> operation(a, b, c)));
它将调用 'main' class:
的这个简单方法private void operation(String name, int age, double salary) {
System.out.format("%s %d %.0f\n", name, age, salary);
}
当然需要这样的辅助类型:
/** Extending interfaces must have a method called consume with N args */
interface NConsumer {}
/*
* Method must be called consume for reflection.
*
* You can define N interfaces like this.
*/
nterface TriConsumer<A, B, C> extends NConsumer {
void consume(A a, B b, C c);
}
interface ArrayCaller<E extends NConsumer> {
void call(E code);
static <T extends NConsumer> ArrayCaller<T> convert(Supplier<?>...argSuppliers) {
final Object[] args = new Object[argSuppliers.length];
for (int i = 0; i < argSuppliers.length; i++) {
args[i] = argSuppliers[i].get();
}
return new ArrayCaller<T>() {
@Override
public void call(T code) {
for (Method m: code.getClass().getMethods()) {
if (m.getName().equals("consume")) {
try {
m.invoke(code, args);
} catch (IllegalAccessException
| IllegalArgumentException
| InvocationTargetException e) {
throw new RuntimeException(e);
}
}
}
}
};
}
}
这里按照自己的方式重新设计了user270349的答案,没有反思。通过静态导入,它可以像这样使用:
Stream.of(new Employee("Adam", 38, 3000), new Employee("John", 19, 2000))
.map(e -> retrieve(e::getName, e::getAge, e::getSalary))
.forEach(c -> c.call(this::printNameAgeSalary));
或者像这样:
Stream.of(new Employee("Adam", 38, 3000), new Employee("John", 19, 2000))
.forEach(e -> retrieve(e::getName, e::getAge).call(this::printNameAge));
代码如下:
private void printNameAgeSalary(String name, int age, double salary) {
System.out.format("%s %d %.0f\n", name, age, salary);
}
private void printNameAge(String name, int age) {
System.out.format("%s %d\n", name, age);
}
public interface Consumer2<T1, T2> {
void consume(T1 t1, T2 t2);
}
public interface Consumer3<T1, T2, T3> {
void consume(T1 t1, T2 t2, T3 t3);
}
public interface Caller<E> {
void call(E code);
static <S1, S2, S3> Caller<Consumer3<S1, S2, S3>> retrieve(Supplier<S1> s1, Supplier<S2> s2, Supplier<S3> s3) {
return (Consumer3<S1, S2, S3> code) -> code.consume(s1.get(), s2.get(), s3.get());
}
static <S1, S2> Caller<Consumer2<S1, S2>> retrieve(Supplier<S1> s1, Supplier<S2> s2) {
return (Consumer2<S1, S2> code) -> code.consume(s1.get(), s2.get());
}
}