Add Memoizer(Function) and Memoizer(Function, boolean).
This commit is contained in:
parent
85751a118e
commit
389fb37c56
|
@ -123,6 +123,7 @@ The <action> type attribute can be add,update,fix,remove.
|
||||||
<action type="add" dev="ggregory" due-to="Gary Gregory">Add JavaVersion.JAVA_18.</action>
|
<action type="add" dev="ggregory" due-to="Gary Gregory">Add JavaVersion.JAVA_18.</action>
|
||||||
<action type="add" dev="ggregory" due-to="Gary Gregory">Add TimeZones.toTimeZone(TimeZone).</action>
|
<action type="add" dev="ggregory" due-to="Gary Gregory">Add TimeZones.toTimeZone(TimeZone).</action>
|
||||||
<action type="add" dev="ggregory" due-to="Gary Gregory">Add FutureTasks.</action>
|
<action type="add" dev="ggregory" due-to="Gary Gregory">Add FutureTasks.</action>
|
||||||
|
<action type="add" dev="ggregory" due-to="Gary Gregory">Add Memoizer(Function) and Memoizer(Function, boolean).</action>
|
||||||
<!-- UPDATE -->
|
<!-- UPDATE -->
|
||||||
<action type="update" dev="ggregory" due-to="Dependabot, Gary Gregory">Bump spotbugs-maven-plugin from 4.2.0 to 4.5.0.0 #735, #808, #822, #834.</action>
|
<action type="update" dev="ggregory" due-to="Dependabot, Gary Gregory">Bump spotbugs-maven-plugin from 4.2.0 to 4.5.0.0 #735, #808, #822, #834.</action>
|
||||||
<action type="update" dev="ggregory" due-to="Dependabot, XenoAmess">Bump actions/cache from v2.1.4 to v2.1.7 #742, #752, #764, #833.</action>
|
<action type="update" dev="ggregory" due-to="Dependabot, XenoAmess">Bump actions/cache from v2.1.4 to v2.1.7 #742, #752, #764, #833.</action>
|
||||||
|
|
|
@ -55,7 +55,7 @@ public class Memoizer<I, O> implements Computable<I, O> {
|
||||||
* Constructs a Memoizer for the provided Computable calculation.
|
* Constructs a Memoizer for the provided Computable calculation.
|
||||||
* </p>
|
* </p>
|
||||||
* <p>
|
* <p>
|
||||||
* If a calculation is thrown an exception for any reason, this exception will be cached and returned for all future
|
* If a calculation throws an exception for any reason, this exception will be cached and returned for all future
|
||||||
* calls with the provided parameter.
|
* calls with the provided parameter.
|
||||||
* </p>
|
* </p>
|
||||||
*
|
*
|
||||||
|
@ -80,6 +80,38 @@ public class Memoizer<I, O> implements Computable<I, O> {
|
||||||
this.mappingFunction = k -> FutureTasks.run(() -> computable.compute(k));
|
this.mappingFunction = k -> FutureTasks.run(() -> computable.compute(k));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Constructs a Memoizer for the provided Function calculation.
|
||||||
|
* </p>
|
||||||
|
* <p>
|
||||||
|
* If a calculation throws an exception for any reason, this exception will be cached and returned for all future
|
||||||
|
* calls with the provided parameter.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param function the function whose results should be memorized
|
||||||
|
* @since 2.13.0
|
||||||
|
*/
|
||||||
|
public Memoizer(final Function<I, O> function) {
|
||||||
|
this(function, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* Constructs a Memoizer for the provided Function calculation, with the option of whether a Function that
|
||||||
|
* experiences an error should recalculate on subsequent calls or return the same cached exception.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param function the computation whose results should be memorized
|
||||||
|
* @param recalculate determines whether the computation should be recalculated on subsequent calls if the previous call
|
||||||
|
* failed
|
||||||
|
* @since 2.13.0
|
||||||
|
*/
|
||||||
|
public Memoizer(final Function<I, O> function, final boolean recalculate) {
|
||||||
|
this.recalculate = recalculate;
|
||||||
|
this.mappingFunction = k -> FutureTasks.run(() -> function.apply(k));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>
|
* <p>
|
||||||
* This method will return the result of the calculation and cache it, if it has not previously been calculated.
|
* This method will return the result of the calculation and cache it, if it has not previously been calculated.
|
||||||
|
|
|
@ -25,7 +25,7 @@ import org.easymock.EasyMock;
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
public class MemoizerTest {
|
public class MemoizerComputableTest {
|
||||||
|
|
||||||
private Computable<Integer, Integer> computable;
|
private Computable<Integer, Integer> computable;
|
||||||
|
|
||||||
|
@ -34,17 +34,6 @@ public class MemoizerTest {
|
||||||
computable = EasyMock.mock(Computable.class);
|
computable = EasyMock.mock(Computable.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testOnlyCallComputableOnceIfDoesNotThrowException() throws Exception {
|
|
||||||
final Integer input = 1;
|
|
||||||
final Memoizer<Integer, Integer> memoizer = new Memoizer<>(computable);
|
|
||||||
expect(computable.compute(input)).andReturn(input);
|
|
||||||
replay(computable);
|
|
||||||
|
|
||||||
assertEquals(input, memoizer.compute(input), "Should call computable first time");
|
|
||||||
assertEquals(input, memoizer.compute(input), "Should not call the computable the second time");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testDefaultBehaviourNotToRecalculateExecutionExceptions() throws Exception {
|
public void testDefaultBehaviourNotToRecalculateExecutionExceptions() throws Exception {
|
||||||
final Integer input = 1;
|
final Integer input = 1;
|
||||||
|
@ -83,14 +72,14 @@ public class MemoizerTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testWhenComputableThrowsRuntimeException() throws Exception {
|
public void testOnlyCallComputableOnceIfDoesNotThrowException() throws Exception {
|
||||||
final Integer input = 1;
|
final Integer input = 1;
|
||||||
final Memoizer<Integer, Integer> memoizer = new Memoizer<>(computable);
|
final Memoizer<Integer, Integer> memoizer = new Memoizer<>(computable);
|
||||||
final RuntimeException runtimeException = new RuntimeException("Some runtime exception");
|
expect(computable.compute(input)).andReturn(input);
|
||||||
expect(computable.compute(input)).andThrow(runtimeException);
|
|
||||||
replay(computable);
|
replay(computable);
|
||||||
|
|
||||||
assertThrows(RuntimeException.class, () -> memoizer.compute(input));
|
assertEquals(input, memoizer.compute(input), "Should call computable first time");
|
||||||
|
assertEquals(input, memoizer.compute(input), "Should not call the computable the second time");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -103,4 +92,15 @@ public class MemoizerTest {
|
||||||
|
|
||||||
assertThrows(Error.class, () -> memoizer.compute(input));
|
assertThrows(Error.class, () -> memoizer.compute(input));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testWhenComputableThrowsRuntimeException() throws Exception {
|
||||||
|
final Integer input = 1;
|
||||||
|
final Memoizer<Integer, Integer> memoizer = new Memoizer<>(computable);
|
||||||
|
final RuntimeException runtimeException = new RuntimeException("Some runtime exception");
|
||||||
|
expect(computable.compute(input)).andThrow(runtimeException);
|
||||||
|
replay(computable);
|
||||||
|
|
||||||
|
assertThrows(RuntimeException.class, () -> memoizer.compute(input));
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -0,0 +1,108 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.apache.commons.lang3.concurrent;
|
||||||
|
|
||||||
|
import static org.easymock.EasyMock.expect;
|
||||||
|
import static org.easymock.EasyMock.replay;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||||
|
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
import org.easymock.EasyMock;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
public class MemoizerFunctionTest {
|
||||||
|
|
||||||
|
private Function<Integer, Integer> function;
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
public void setUpComputableMock() {
|
||||||
|
function = EasyMock.mock(Function.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDefaultBehaviourNotToRecalculateExecutionExceptions() throws Exception {
|
||||||
|
final Integer input = 1;
|
||||||
|
final Memoizer<Integer, Integer> memoizer = new Memoizer<>(function);
|
||||||
|
final IllegalArgumentException interruptedException = new IllegalArgumentException();
|
||||||
|
expect(function.apply(input)).andThrow(interruptedException);
|
||||||
|
replay(function);
|
||||||
|
|
||||||
|
assertThrows(Throwable.class, () -> memoizer.compute(input));
|
||||||
|
assertThrows(IllegalArgumentException.class, () -> memoizer.compute(input));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDoesNotRecalculateWhenSetToFalse() throws Exception {
|
||||||
|
final Integer input = 1;
|
||||||
|
final Memoizer<Integer, Integer> memoizer = new Memoizer<>(function, false);
|
||||||
|
final IllegalArgumentException interruptedException = new IllegalArgumentException();
|
||||||
|
expect(function.apply(input)).andThrow(interruptedException);
|
||||||
|
replay(function);
|
||||||
|
|
||||||
|
assertThrows(Throwable.class, () -> memoizer.compute(input));
|
||||||
|
assertThrows(IllegalArgumentException.class, () -> memoizer.compute(input));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDoesRecalculateWhenSetToTrue() throws Exception {
|
||||||
|
final Integer input = 1;
|
||||||
|
final Integer answer = 3;
|
||||||
|
final Memoizer<Integer, Integer> memoizer = new Memoizer<>(function, true);
|
||||||
|
final IllegalArgumentException interruptedException = new IllegalArgumentException();
|
||||||
|
expect(function.apply(input)).andThrow(interruptedException).andReturn(answer);
|
||||||
|
replay(function);
|
||||||
|
|
||||||
|
assertThrows(Throwable.class, () -> memoizer.compute(input));
|
||||||
|
assertEquals(answer, memoizer.compute(input));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testOnlyCallComputableOnceIfDoesNotThrowException() throws Exception {
|
||||||
|
final Integer input = 1;
|
||||||
|
final Memoizer<Integer, Integer> memoizer = new Memoizer<>(function);
|
||||||
|
expect(function.apply(input)).andReturn(input);
|
||||||
|
replay(function);
|
||||||
|
|
||||||
|
assertEquals(input, memoizer.compute(input), "Should call computable first time");
|
||||||
|
assertEquals(input, memoizer.compute(input), "Should not call the computable the second time");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testWhenComputableThrowsError() throws Exception {
|
||||||
|
final Integer input = 1;
|
||||||
|
final Memoizer<Integer, Integer> memoizer = new Memoizer<>(function);
|
||||||
|
final Error error = new Error();
|
||||||
|
expect(function.apply(input)).andThrow(error);
|
||||||
|
replay(function);
|
||||||
|
|
||||||
|
assertThrows(Error.class, () -> memoizer.compute(input));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testWhenComputableThrowsRuntimeException() throws Exception {
|
||||||
|
final Integer input = 1;
|
||||||
|
final Memoizer<Integer, Integer> memoizer = new Memoizer<>(function);
|
||||||
|
final RuntimeException runtimeException = new RuntimeException("Some runtime exception");
|
||||||
|
expect(function.apply(input)).andThrow(runtimeException);
|
||||||
|
replay(function);
|
||||||
|
|
||||||
|
assertThrows(RuntimeException.class, () -> memoizer.compute(input));
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue