簡體   English   中英

在 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 類中 - 否則太容易了):

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

雖然它遠非美麗。

我認為如果您的someOperationWithEmployeeEmployee對象作為參數會好得多。

更新:對於這值,您可以像這樣使用我的免費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庫,因為它專注於元組並且已經提供了像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”類的這個簡單方法:

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());
    }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM