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 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 Memoizer(Function) and Memoizer(Function, boolean).</action>
|
||||
<!-- 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, 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.
|
||||
* </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.
|
||||
* </p>
|
||||
*
|
||||
|
@ -80,6 +80,38 @@ public class Memoizer<I, O> implements Computable<I, O> {
|
|||
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>
|
||||
* 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.Test;
|
||||
|
||||
public class MemoizerTest {
|
||||
public class MemoizerComputableTest {
|
||||
|
||||
private Computable<Integer, Integer> computable;
|
||||
|
||||
|
@ -34,17 +34,6 @@ public class MemoizerTest {
|
|||
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
|
||||
public void testDefaultBehaviourNotToRecalculateExecutionExceptions() throws Exception {
|
||||
final Integer input = 1;
|
||||
|
@ -83,14 +72,14 @@ public class MemoizerTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testWhenComputableThrowsRuntimeException() throws Exception {
|
||||
public void testOnlyCallComputableOnceIfDoesNotThrowException() 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);
|
||||
expect(computable.compute(input)).andReturn(input);
|
||||
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
|
||||
|
@ -103,4 +92,15 @@ public class MemoizerTest {
|
|||
|
||||
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