You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by bd...@apache.org on 2021/06/21 16:45:32 UTC
[sling-org-apache-sling-graphql-core] 01/01: SLING-10502 - lazy
loading helpers
This is an automated email from the ASF dual-hosted git repository.
bdelacretaz pushed a commit to branch SLING-10502/lazy-loading
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-graphql-core.git
commit 7aea4ef95f4e1e79df341f04399f9c0123ab9519
Author: Bertrand Delacretaz <bd...@apache.org>
AuthorDate: Mon Jun 21 18:42:53 2021 +0200
SLING-10502 - lazy loading helpers
---
.../helpers/layzloading/LazyLoadingField.java | 44 +++
.../helpers/layzloading/LazyLoadingMap.java | 171 ++++++++++++
.../graphql/helpers/layzloading/package-info.java | 26 ++
.../helpers/lazyloading/LazyLoadingFieldTest.java | 65 +++++
.../helpers/lazyloading/LazyLoadingMapTest.java | 300 +++++++++++++++++++++
5 files changed, 606 insertions(+)
diff --git a/src/main/java/org/apache/sling/graphql/helpers/layzloading/LazyLoadingField.java b/src/main/java/org/apache/sling/graphql/helpers/layzloading/LazyLoadingField.java
new file mode 100644
index 0000000..83ce1f6
--- /dev/null
+++ b/src/main/java/org/apache/sling/graphql/helpers/layzloading/LazyLoadingField.java
@@ -0,0 +1,44 @@
+/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ ~ 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.sling.graphql.helpers.layzloading;
+
+import java.util.function.Supplier;
+
+/** Helper for a single lazy-loading value */
+public class LazyLoadingField<T> {
+ private Supplier<T> supplier;
+ private T value;
+
+ public LazyLoadingField(Supplier<T> supplier) {
+ this.supplier = supplier;
+ }
+
+ public T get() {
+ if(value == null) {
+ synchronized(this) {
+ if(value == null && supplier != null) {
+ value = supplier.get();
+ }
+ supplier = null;
+ }
+ }
+ return value;
+ }
+}
diff --git a/src/main/java/org/apache/sling/graphql/helpers/layzloading/LazyLoadingMap.java b/src/main/java/org/apache/sling/graphql/helpers/layzloading/LazyLoadingMap.java
new file mode 100644
index 0000000..1a70e62
--- /dev/null
+++ b/src/main/java/org/apache/sling/graphql/helpers/layzloading/LazyLoadingMap.java
@@ -0,0 +1,171 @@
+/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ ~ 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.sling.graphql.helpers.layzloading;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.function.Supplier;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class LazyLoadingMap<K, T> extends HashMap<K, T> {
+
+ private final Logger log = LoggerFactory.getLogger(getClass());
+ private final Map<K, Supplier<T>> suppliers = new HashMap<>();
+ private int suppliersCallCount;
+
+ /** Calls computeAll - should be avoided if possible */
+ @Override
+ public boolean equals(Object o) {
+ if(!(o instanceof LazyLoadingMap)) {
+ return false;
+ }
+ final LazyLoadingMap<?,?> other = (LazyLoadingMap<?,?>)o;
+
+ // Equality seems complicated to compute without this
+ computeAll();
+ return super.equals(other);
+ }
+
+ @Override
+ public int hashCode() {
+ return super.hashCode() + suppliers.hashCode();
+ }
+
+ /** Adds a Supplier that provides a lazy loaded value
+ * @return this object, to be able to chain calls
+ */
+ public LazyLoadingMap<K, T> withSupplier(K key, Supplier<T> supplier) {
+ suppliers.put(key, supplier);
+ return this;
+ }
+
+ @Override
+ public T get(Object key) {
+ return lazyCompute(key);
+ }
+
+ @SuppressWarnings("unchecked")
+ private T lazyCompute(Object key) {
+ if(key == null) {
+ return null;
+ }
+ T value = super.get(key);
+ if(value == null) {
+ synchronized(this) {
+ if(value == null) {
+ final Supplier<T> s = suppliers.remove(key);
+ if(s != null) {
+ suppliersCallCount++;
+ value = s.get();
+ super.put((K)key, value);
+ }
+ }
+ }
+ }
+ return value;
+ }
+
+
+ /** This indicates how many Supplier calls have been made.
+ * Can be useful in case of doubt, as several methods need
+ * to call computeAll().
+ */
+ public int getSuppliersCallCount() {
+ return suppliersCallCount;
+ }
+
+ @Override
+ public T remove(Object key) {
+ synchronized(this) {
+ lazyCompute(key);
+ return super.remove(key);
+ }
+ }
+
+ @Override
+ public void clear() {
+ synchronized(this) {
+ suppliers.clear();
+ super.clear();
+ }
+ }
+
+ @Override
+ public boolean containsKey(Object key) {
+ return super.containsKey(key) || suppliers.containsKey(key);
+ }
+
+ @Override
+ public int size() {
+ return keySet().size();
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return super.isEmpty() && suppliers.isEmpty();
+ }
+
+ @Override
+ public Set<K> keySet() {
+ final Set<K> result = new HashSet<>();
+ result.addAll(super.keySet());
+ result.addAll(suppliers.keySet());
+ return result;
+ }
+
+ /** Required for some methods that need all our values
+ * Calling those methods should be avoided if possible
+ */
+ private void computeAll() {
+ log.info("computeAll called, all remaining lazy values will be evaluated now");
+ suppliers.entrySet().forEach(e -> {
+ if(!super.containsKey(e.getKey())) {
+ suppliersCallCount++;
+ put(e.getKey(), e.getValue().get());
+ }
+ });
+ }
+
+ /** Calls computeAll - should be avoided if possible */
+ @Override
+ public Collection<T> values() {
+ computeAll();
+ return super.values();
+ }
+
+ /** Calls computeAll - should be avoided if possible */
+ @Override
+ public Set<Entry<K, T>> entrySet() {
+ computeAll();
+ return super.entrySet();
+ }
+
+ /** Calls computeAll - should be avoided if possible */
+ @Override
+ public boolean containsValue(Object value) {
+ computeAll();
+ return super.containsValue(value);
+ }
+}
diff --git a/src/main/java/org/apache/sling/graphql/helpers/layzloading/package-info.java b/src/main/java/org/apache/sling/graphql/helpers/layzloading/package-info.java
new file mode 100644
index 0000000..b43f77a
--- /dev/null
+++ b/src/main/java/org/apache/sling/graphql/helpers/layzloading/package-info.java
@@ -0,0 +1,26 @@
+/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ ~ 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.
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
+
+ /**
+ * This package contains helpers which are independent of
+ * a specific implementation of the underlying graphQL engine.
+ */
+@Version("0.0.1")
+package org.apache.sling.graphql.helpers.layzloading;
+import org.osgi.annotation.versioning.Version;
diff --git a/src/test/java/org/apache/sling/graphql/helpers/lazyloading/LazyLoadingFieldTest.java b/src/test/java/org/apache/sling/graphql/helpers/lazyloading/LazyLoadingFieldTest.java
new file mode 100644
index 0000000..52608fe
--- /dev/null
+++ b/src/test/java/org/apache/sling/graphql/helpers/lazyloading/LazyLoadingFieldTest.java
@@ -0,0 +1,65 @@
+/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ ~ 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.sling.graphql.helpers.lazyloading;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+
+import java.util.UUID;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.function.Supplier;
+
+import org.apache.sling.graphql.helpers.layzloading.LazyLoadingField;
+import org.junit.Test;
+
+public class LazyLoadingFieldTest {
+ @Test
+ public void basicTest() {
+ final AtomicInteger nCalls = new AtomicInteger();
+
+ final Supplier<String> sup = () -> {
+ nCalls.incrementAndGet();
+ return UUID.randomUUID().toString();
+ };
+
+ final LazyLoadingField<String> f = new LazyLoadingField<>(sup);
+ assertEquals(0, nCalls.get());
+ final String firstValue = f.get();
+ assertEquals(1, nCalls.get());
+ for(int i=0; i < 42; i++) {
+ assertEquals(firstValue, f.get());
+ }
+ assertEquals(1, nCalls.get());
+ }
+
+ @Test
+ public void nullSupplier() {
+ final AtomicInteger nCalls = new AtomicInteger();
+ final Supplier<Double> nullSup = () -> {
+ nCalls.incrementAndGet();
+ return null;
+ };
+ final LazyLoadingField<Double> f = new LazyLoadingField<>(nullSup);
+ assertEquals(0, nCalls.get());
+ assertNull(f.get());
+ assertEquals(1, nCalls.get());
+ assertNull(f.get());
+ assertEquals(1, nCalls.get());
+ }
+}
diff --git a/src/test/java/org/apache/sling/graphql/helpers/lazyloading/LazyLoadingMapTest.java b/src/test/java/org/apache/sling/graphql/helpers/lazyloading/LazyLoadingMapTest.java
new file mode 100644
index 0000000..c886303
--- /dev/null
+++ b/src/test/java/org/apache/sling/graphql/helpers/lazyloading/LazyLoadingMapTest.java
@@ -0,0 +1,300 @@
+/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ ~ 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.sling.graphql.helpers.lazyloading;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import java.util.HashSet;
+import java.util.Set;
+import java.util.UUID;
+import java.util.function.Supplier;
+
+import org.apache.sling.graphql.helpers.layzloading.LazyLoadingMap;
+import org.junit.Before;
+import org.junit.Test;
+
+public class LazyLoadingMapTest {
+ private static final String TEST_STRING = "Fritz Frisst etc. etc.";
+ private int counter;
+ private Supplier<String> lazyCounter = () -> "X" + String.valueOf(++counter);
+
+ @Before
+ public void setup() {
+ counter = 0;
+ }
+
+ @Test
+ public void basicTest() {
+ final LazyLoadingMap<Integer, String> map = new LazyLoadingMap<>();
+ assertNull(map.get(42));
+
+ map
+ .withSupplier(21, () -> UUID.randomUUID().toString())
+ .withSupplier(42, () -> TEST_STRING)
+ ;
+ assertEquals(0, map.getSuppliersCallCount());
+ assertEquals(TEST_STRING, map.get(42));
+ assertEquals(1, map.getSuppliersCallCount());
+ final String random = map.get(21);
+ assertNotNull(random);
+ assertEquals(random, map.get(21));
+ assertEquals(2, map.getSuppliersCallCount());
+ }
+
+ @Test
+ public void addAlsoWorks() {
+ final LazyLoadingMap<Integer, String> map = new LazyLoadingMap<>();
+ map.put(42, TEST_STRING);
+ assertEquals(TEST_STRING, map.get(42));
+ map.withSupplier(42, () -> "should not change");
+ assertEquals(TEST_STRING, map.get(42));
+ assertEquals(0, map.getSuppliersCallCount());
+ }
+
+ @Test
+ public void remove() {
+ final LazyLoadingMap<Integer, String> map = new LazyLoadingMap<>();
+ map.withSupplier(21, lazyCounter);
+ map.put(42, TEST_STRING);
+ assertEquals(2, map.size());
+ assertEquals(TEST_STRING, map.get(42));
+ assertEquals(TEST_STRING, map.remove(42));
+ assertNull(map.get(42));
+ assertEquals(1, map.size());
+ assertEquals(0, map.getSuppliersCallCount());
+
+ // Remove before and after computing
+ assertEquals(0, map.getSuppliersCallCount());
+ map.withSupplier(112, lazyCounter);
+ map.withSupplier(113, lazyCounter);
+ assertEquals("X1", map.get(113));
+ assertEquals("X1", map.remove(113));
+ assertEquals(1, map.getSuppliersCallCount());
+ assertEquals("X2", map.remove(112));
+ assertEquals(2, map.getSuppliersCallCount());
+ }
+
+ @Test
+ public void containsValueComputesEverything() {
+ final LazyLoadingMap<Integer, String> map = new LazyLoadingMap<>();
+ assertFalse(map.containsKey(42));
+ assertEquals(0, map.getSuppliersCallCount());
+
+ assertFalse(map.containsValue("X1"));
+ map.withSupplier(42, lazyCounter);
+ assertTrue(map.containsValue("X1"));
+
+ assertFalse(map.containsValue("X2"));
+ map.withSupplier(21, lazyCounter);
+ assertEquals(1, map.getSuppliersCallCount());
+ assertTrue(map.containsValue("X1"));
+ assertTrue(map.containsValue("X2"));
+ assertEquals(2, map.getSuppliersCallCount());
+
+ assertFalse(map.containsValue(TEST_STRING));
+ map.put(71, TEST_STRING);
+ map
+ .withSupplier(92, lazyCounter)
+ .withSupplier(93, lazyCounter)
+ ;
+ assertTrue(map.containsValue(TEST_STRING));
+ assertTrue(map.containsValue("X1"));
+ assertTrue(map.containsValue("X2"));
+ assertTrue(map.containsValue("X3"));
+ assertTrue(map.containsValue("X4"));
+ assertFalse(map.containsValue("X5"));
+
+ assertEquals(4, map.getSuppliersCallCount());
+ }
+
+ @Test
+ public void containsKey() {
+ final LazyLoadingMap<Integer, String> map = new LazyLoadingMap<>();
+ assertFalse(map.containsKey(42));
+ assertEquals(0, map.getSuppliersCallCount());
+
+ map.withSupplier(42, lazyCounter);
+ map.put(21, "nothing");
+
+ assertTrue(map.containsKey(42));
+ assertTrue(map.containsKey(21));
+ assertFalse(map.containsKey(22));
+
+ assertEquals(0, map.getSuppliersCallCount());
+ }
+
+ @Test
+ public void keySet() {
+ final LazyLoadingMap<Integer, String> map = new LazyLoadingMap<>();
+ assertEquals(0, map.keySet().size());
+ map.put(112, "nothing");
+ assertEquals(1, map.keySet().size());
+ map.withSupplier(21, lazyCounter);
+ map.withSupplier(42, lazyCounter);
+ map.withSupplier(110, lazyCounter);
+
+ assertEquals(0, map.getSuppliersCallCount());
+ map.get(42);
+ assertEquals(1, map.getSuppliersCallCount());
+
+ final Set<Integer> ks = map.keySet();
+ assertEquals(4, ks.size());
+ assertTrue(ks.contains(21));
+ assertTrue(ks.contains(42));
+ assertTrue(ks.contains(112));
+ assertTrue(ks.contains(110));
+ assertEquals(1, map.getSuppliersCallCount());
+ }
+
+ @Test
+ public void entrySet() {
+ final LazyLoadingMap<Integer, String> map = new LazyLoadingMap<>();
+ map.put(112, TEST_STRING);
+ map.withSupplier(21, lazyCounter);
+ map.withSupplier(42, lazyCounter);
+
+ final Set<String> toFind = new HashSet<>();
+ toFind.add(TEST_STRING);
+ toFind.add("X1");
+ toFind.add("X2");
+
+ assertEquals(3, toFind.size());
+ map.entrySet().forEach(e -> toFind.remove(e.getValue()));
+ assertEquals(0, toFind.size());
+ assertEquals(2, map.getSuppliersCallCount());
+ }
+
+ @Test
+ public void values() {
+ final LazyLoadingMap<Integer, String> map = new LazyLoadingMap<>();
+ map.put(112, TEST_STRING);
+ map.withSupplier(21, lazyCounter);
+ map.withSupplier(42, lazyCounter);
+
+ final Set<String> toFind = new HashSet<>();
+ toFind.add(TEST_STRING);
+ toFind.add("X1");
+ toFind.add("X2");
+
+ assertEquals(3, toFind.size());
+ assertEquals(0, map.getSuppliersCallCount());
+ map.values().forEach(v -> toFind.remove(v));
+ assertEquals(0, toFind.size());
+ assertEquals(2, map.getSuppliersCallCount());
+ }
+
+ @Test
+ public void isEmpty() {
+ final LazyLoadingMap<Integer, String> map = new LazyLoadingMap<>();
+ assertTrue(map.isEmpty());
+
+ map.put(112, TEST_STRING);
+ assertFalse(map.isEmpty());
+ map.withSupplier(42, lazyCounter);
+ assertFalse(map.isEmpty());
+
+ map.remove(112);
+ assertFalse(map.isEmpty());
+ map.remove(42);
+ assertTrue(map.isEmpty());
+
+ assertEquals(1, map.getSuppliersCallCount());
+ }
+
+ @Test
+ public void nullSupplier() {
+ final LazyLoadingMap<Integer, String> map = new LazyLoadingMap<>();
+ final Supplier<String> nullSup = () -> null;
+
+ map.withSupplier(42, nullSup);
+ assertEquals(0, map.getSuppliersCallCount());
+ assertEquals(1, map.size());
+
+ assertNull(map.get(42));
+ assertEquals(1, map.getSuppliersCallCount());
+ assertEquals(1, map.size());
+
+ assertNull(map.get(42));
+ assertEquals(1, map.getSuppliersCallCount());
+ assertEquals(1, map.size());
+ }
+
+ @Test
+ public void clear() {
+ final LazyLoadingMap<Integer, String> map = new LazyLoadingMap<>();
+ map.withSupplier(21, lazyCounter);
+ map.withSupplier(42, lazyCounter);
+ assertEquals("X1", map.get(42));
+ assertEquals(1, map.getSuppliersCallCount());
+ assertEquals(2, map.size());
+ map.clear();
+ assertEquals(0, map.size());
+ assertNull(map.get(42));
+ assertEquals(1, map.getSuppliersCallCount());
+ }
+
+ @Test
+ public void testHashCode() {
+ final LazyLoadingMap<Integer, String> map = new LazyLoadingMap<>();
+ int hc = map.hashCode();
+ map.put(21, TEST_STRING);
+ assertNotEquals(hc, hashCode());
+ hc = map.hashCode();
+ map.withSupplier(42, lazyCounter);
+ assertNotEquals(hc, hashCode());
+ }
+
+ @Test
+ public void testEquals() {
+ final Supplier<String> constant = () -> "Some String";
+ final LazyLoadingMap<Integer, String> A = new LazyLoadingMap<>();
+ final LazyLoadingMap<Integer, String> B = new LazyLoadingMap<>();
+ assertEquals(A, B);
+
+ A.withSupplier(42, constant);
+ A.put(21, TEST_STRING);
+ assertNotEquals(A, B);
+ assertEquals(1, A.getSuppliersCallCount());
+ assertEquals(0, B.getSuppliersCallCount());
+
+ B.withSupplier(42, constant);
+ assertNotEquals(A, B);
+ B.put(21, TEST_STRING);
+ assertEquals(B, A);
+ assertEquals(1, A.getSuppliersCallCount());
+ assertEquals(1, B.getSuppliersCallCount());
+ }
+
+ @Test
+ public void nullKey() {
+ final LazyLoadingMap<Integer, String> map = new LazyLoadingMap<>();
+ assertNull(map.get(null));
+ }
+
+ @Test
+ public void applesAndOranges() {
+ final LazyLoadingMap<Integer, String> map = new LazyLoadingMap<>();
+ assertNotEquals(map, "A string");
+ }
+}