在Java流中拆分对象
我想知道是否有可能分割流中的对象。例如,对于该在Java流中拆分对象,java,java-8,java-stream,method-reference,Java,Java 8,Java Stream,Method Reference,我想知道是否有可能分割流中的对象。例如,对于该员工: 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 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类中-否则太容易了):
现在看起来是这样的:
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));
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));
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));
问题是,是否有可能在这样的流中放入一些代码
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
是这种情况下最漂亮的解决方案,但有时我们在现实生活中无法做到这一点,应该是lambdas的通用解决方案。简而言之,答案是否定的,您无法做到这一点。我能想到的最短解决方案是定义自己的功能接口,如下所示:
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));
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));
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));
虽然它一点也不漂亮
我认为如果您的someOperationWithEmployee
将Employee
对象作为参数会更好
更新:对于这对值,您可以像这样使用我的免费库:
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));
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));
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));
但是,它仅限于对,因此不能以这种方式处理三个或更多的值(我不打算添加这样的函数)
我还检查了这个库,因为它集中在元组上,并且已经提供了类似的接口。然而,似乎也没有简单的方法来解决您的问题。我不确定这是否适合您的需要,但它需要一点反射,并且不检查某些类型 您可以通过以下方式运行我的解决方案:
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)));
当然,它需要以下辅助类型:
/** 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);
}
}
}
}
};
}
}
/**扩展接口必须有一个名为consume的方法,并带有N个参数*/
接口NConsumer{}
/*
*必须调用方法进行反射。
*
*您可以这样定义N个接口。
*/
界面三角消费者{
无效消耗(A、B、C);
}
接口阵列调谐器{
无效呼叫(E代码);
静态ArrayCaller转换(供应商…argSuppliers){
最终对象[]args=新对象[argSuppliers.length];
对于(int i=0;i
这里以自己的方式重新设计,没有任何反思。使用静态导入时,可以这样使用:
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());
}
}
private void printNameAgeAlary(字符串名称、整数年龄、双倍工资){
系统输出格式(“%s%d%.0f\n”,姓名、年龄、工资);
}
私有void printNameAge(字符串名,int-age){
System.out.format(“%s%d\n”,名称,年龄);
}
公共接口使用者2{
空洞消耗(T1,T2);
}
公共接口使用者3{
空洞消耗(T1、T2、T3);
}
公共接口调用者{
无效呼叫(E代码);
静态调用方检索(供应商s1、供应商s2、供应商s3){
return(Consumer3 code)->code.consumer(s1.get()、s2.get()、s3.get());
}
静态调用方检索(供应商s1、供应商s2){
return(Consumer2代码)->code.consumer(s1.get(),s2.get());
}
}
我相信这将是一个更大的麻烦。是的,我知道javaslang,但似乎它在这里也帮不上忙。@Tagir在我看来,jOOL是一个宽泛而繁重的解决方案,因为只有Function3 FunctionN实现。也许有一些小的库可以用于这种特殊和流行的情况(我指的是具有多个参数的函数)?我想最好自己为您的项目创建这样的接口。在我看来,目前这种接口还没有“事实上的标准”。你可以使用this::someOperationWithEmployee
,这样会更好一些。@StuartMarks,当然,我通常是这样写的,但OP要求使用lambda符号。我想在回答中明确显示someOperationWithEmployee
方法需要多少参数。我认为最好为1、2、3等参数定义单独的方法,直到达到合理的限制,这样您就可以进行编译时类型检查,而不必显式强制转换。而且它的工作速度会大大加快。对,我考虑过为预定义的XCosumer接口调用call
方法,但对于NConsumer,也可以使用用户定义的XCosumer接口调用任何其他数量的参数。编辑:我将ArrayCaller设置为泛型,这样您就可以键入它,而无需再键入lambda。@TagirValeev为了加快速度,您还可以缓存反射,保存按接口类型(如TriConsumer)索引的方法对象。通过一些包装和重命名,它允许以以下方式写入:Stream.of(…).forEach(e->retrieve(e::getName,e::getAge,e::getSalary)。然后调用(this::someOperationWithEmployee))代码>与当前比较:Stream.of(..).forEach(e->someOperationWithEmployee(e.getName,e.getAge(),e.getSalary))代码>