public Map<K, V> getAll(Iterable<K> keys) {
    List<K> missingKeys = new ArrayList<>();
    Map<K, V> result = new HashMap<>();
    for (K key : keys) {
      V value = map.get(key);
      if (value == null && !map.containsKey(key)) {
        missingKeys.add(key);
      } else {
        result.put(key, value);
      }
    }
    if (!missingKeys.isEmpty()) {
      Map<K, V> missingValues = loader.loadAll(missingKeys);
      map.putAll(missingValues);
      result.putAll(missingValues);
      for (K missingKey : missingKeys) {
        if (!map.containsKey(missingKey)) {
          map.put(missingKey, null);
          result.put(missingKey, null);
        }
      }
    }
    return result;
  }
@Test
  public void getAllNullable() {
    // ask for 3 keys but only 2 are available in backed (third key is missing)
    List<String> keys = Arrays.asList("one", "two", "three");
    Map<String, String> values = new HashMap<>();
    values.put("one", "un");
    values.put("two", "deux");
    when(loader.loadAll(keys)).thenReturn(values);
    assertThat(cache.getAll(keys))
      .hasSize(3)
      .containsEntry("one", "un")
      .containsEntry("two", "deux")
      .containsEntry("three", null);

    // ask for 4 keys. Only a single one was never loaded. The 3 others are kept from cache
    when(loader.loadAll(Arrays.asList("four"))).thenReturn(ImmutableMap.of("four", "quatre"));
    assertThat(cache.getAll(Arrays.asList("one", "two", "three", "four")))
      .hasSize(4)
      .containsEntry("one", "un")
      .containsEntry("two", "deux")
      .containsEntry("three", null)
      .containsEntry("four", "quatre");
    verify(loader, times(2)).loadAll(anyCollection());
  }