Lazy but persistent, java 8 lambda rating

I'm currently working on creating my own custom array in java that uses a binary search tree to store a collection of values.

I want to add a map method that takes Functionas an argument to create a new array. I do not want to evaluate functions unless a specific value is requested. This is pretty easy to do, as lambdas are lazy priced. However, I also want the function to be evaluated only once, even if the result is requested multiple times.

I could create a node that stores the provider and updates the result when evaluating:

class Node<T> {

    private T value;
    private Supplier<T> supplier;

    public T get() {
        if (null != value)
            return value;
        value = supplier.get();
        return value;
    }
}

... where is supplierobtained from Function, applied to the value in the old version of the stored array.

*. , **.

- Node get:

class Node<T> {

    private final Optional<T> value;
    private final Supplier<T> supplier;

    Node(Supplier<T> supplier, T value) {
        this.supplier = supplier;
        this.value = Optional.ofNullable(value);
    }

    public Tuple<Node<T>, T> get() {
        if (null != value)
            return new Tuple<>(this, value.orElse(null));
        T result = supplier.get();
        Node<T> newNode = new Node<>(null, result);
        return new Tuple<>(newNode, result);
    }
}

, ; , . .

- , , , ? .

* , , .

** value a Optional<T>, null , , Optional.empty(), , null. , .

, , , , . . ( 32- ) , .

EDIT:

github. .

+5
3

: , Supplier, Optional Node. , .


, //. memoize , :

memoization memoisation - , ,

, , - (, memoization):

public static <T, U> Function<T, U> memoize(Function<T, U> function) {
    Map<T, U> cache = new ConcurrentHashMap<>();
    return input -> cache.computeIfAbsent(input, function::apply);
}

, , . memoize :

// This method takes quite long to execute
Integer longCalculation(Integer x) {
    try {
        Thread.sleep(1_000);
    } catch (InterruptedException ignored) {
    }
    return x * 2;
}

// Our function is a method reference to the method above
Function<Integer, Integer> function = this::longCalculation;

// Now we memoize the function declared above
Function<Integer, Integer> memoized = memoize(function);

, :

int result1 = function.apply(1);
int result2 = function.apply(2);
int result3 = function.apply(3);
int result4 = function.apply(2);
int result5 = function.apply(1);

, ~ 5 (1 ).

, memoized 1 2 3 2 1:

int memoizedResult1 = memoized.apply(1);
int memoizedResult2 = memoized.apply(2);
int memoizedResult3 = memoized.apply(3);
int memoizedResult4 = memoized.apply(2); // <-- returned from cache
int memoizedResult5 = memoized.apply(1); // <-- returned from cache

, ~ 3 . , .


, ... map memoize memoized . ConcurrentHashMap.

memoize ConcurrentHashMap , concurrency.

: ... . , , . , . ...;)

+3

, , .

?

class Node<T> {
    private Supplier<T> supplier;

    Node(T value, Supplier<T> supplier) {
        this.supplier = sync(lazy(value, supplier));
    }

    public T get() {
        return supplier.get();
    }
}

sync Supplier , target, lock :

static <T> Supplier<T> sync(Supplier<T> target) {
    return sync(new ReentrantLock(), target);
}

static <T> Supplier<T> sync(ReentrantLock lock, Supplier<T> target) {
    //     v--- synchronizing for multi-threads once
    return lazy(() -> {
        // the interesting thing is that when more than one threads come in here
        // but target.delegate is only changed once
        lock.lock();
        try {  
            return target.get();
        } finally {
            lock.unlock();
        }
    });
}

lazy Supplier , :

static <T> Supplier<T> lazy(T value, Supplier<T> defaults) {
    return lazy(() -> value != null ? value : defaults.get());
}

static <T> Supplier<T> lazy(Supplier<T> target) {
    return new Supplier<T>() {
        private volatile Supplier<T> delegate = () -> {
            T it = target.get();
            //v--- return the evaluated value in turn
            delegate = () -> it;
            return it;
        };

        @Override
        public T get() {
            return delegate.get();
        }
    };
}

IF, , , github, . , lazy once, .

+2

: , , , .

Suppliers # memoize Google-guava.

, , Supplier null, -.

Also note that the method Supplier memoizereturns com.google.base.Supplier, which extends java.util.Supplier, so you can use it to designate it java.util.Supplierso that you don't force your clients (who will use your library) to hang in the guava library.

0
source

Source: https://habr.com/ru/post/1679058/


All Articles