java.util.function.Consumer andThen方法有效的内存使用情况

时间:2017-08-16 21:01:28

标签: java memory-management lambda functional-interface

我一直在查看java FunctionalInterface java.util.function.Consumer,它有源代码

package java.util.function;
import java.util.Objects;

public interface Consumer<T> {
    void accept(T t);

    default Consumer<T> andThen(Consumer<? super T> after) {
        Objects.requireNonNull(after);
        return (T t) -> { accept(t); after.accept(t); };
    }

}

所以,如果我调用consumer1.andThen(consumer2)...和then(consumerN).accept(); 在我看来,这有效地创造了消费者对象的N +(N - 1)。如果我使用它错误或者我不应该那样使用它,请告诉我。

这是我测试问题的测试代码。我错过了什么吗?

public class Test1 {
    public static int c = 0;

    public static int acceptC = 0;

    public Test1(){
        ++c;
        System.out.println("new Object Created = "+c);
    }

    public void accept(int i){
        acceptC++;
        System.out.println("accept call "+ acceptC);
    }

    public Test1 andThen(Test1 after) {
       return new Test1(){
        @Override
        public void accept(int i) {
            acceptC++;
            System.out.println("accept call in temp = " +acceptC) ;
            Test1.this.accept(++i); after.accept(++i);
        } 

        };
    }

    public static void main(String args[]){
        Test1 t1 = new Test1();
        Test1 t2 = new Test1();
        Test1 t3 = new Test1();
        Test1 t4 = new Test1();
        Test1 t5 = new Test1();

        t1.andThen(t2).andThen(t3).andThen(t4).andThen(t5).accept(0);
        System.out.println("RESULT total Test Object created ="+Test1.c);
    }
}

我得到了Out Put

new Object Created = 1
new Object Created = 2
new Object Created = 3
new Object Created = 4
new Object Created = 5
new Object Created = 6
new Object Created = 7
new Object Created = 8
new Object Created = 9
accept call in temp = 1
accept call in temp = 2
accept call in temp = 3
accept call in temp = 4
accept call 5
accept call 6
accept call 7
accept call 8
accept call 9
RESULT total Test Object created =9

我知道这并不重要,除非你以这种方式处理大量数据.. 但只是想知道

1 个答案:

答案 0 :(得分:0)

有意未指定在将lambda表达式计算为功能接口的实例时是否将创建新对象,另请参阅Does a lambda expression create an object on the heap every time it's executed?

因此,我们不能对为此操作创建的对象实例的数量做出一般性声明,特别是对于可能存在可重用实例的相同代码的重复执行(这可能是重要的情况)。此外,Consumer.andThen的确切实现并不固定,也不必像发布的代码。 default方法可以覆盖也很重要,因此如果您调用Consumer的第一个andThen不是lambda表达式的结果,而是自定义实现的结果,它可能会更改整个结果显着。

实际上,使用当前实现,lambda表达式(T t) -> { accept(t); after.accept(t); }将捕获对其他Consumer个实例的两个引用,因此每个实例都会计算一个新的Consumer个实例时间,就像你的具体类变体一样。因此,此andThen次调用链将创建Consumer个实例的不平衡树,就像您的变体一样。

但是,这并不意味着所有叶子消费者实例都是新实例。在您的示例中,所有叶函数都具有acceptC++; System.out.println("accept call "+ acceptC);形式,这意味着捕获this以便能够修改acceptC,因此每次等效的lambda表达式将评估为新实例。但这不是真实的生活场景。在实践中,您将使用andThen来组合不同的消费者,因此其中一些可能正在捕获,而另一些可能不会捕获,因此其中一些可能会被重用,而另一些则不会。

这就是当前实施的全部内容。未来的实现可能会将不同但相同的lambda表达式折叠到单个实例,或者识别lambda表达式何时捕获对可重用lambda实例的引用以使其可重用。这可能会产生级联效应,从而大大减少了实例数。

你说你已经意识到物体的数量并不重要。但值得注意的是,过度使用andThen会产生一个深度不平衡的消费者树,其accept方法的执行可能需要大量的堆栈深度。