
org.elasticsearch.common.util.Maps Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of elasticsearch Show documentation
Show all versions of elasticsearch Show documentation
Elasticsearch - Open Source, Distributed, RESTful Search Engine
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
package org.elasticsearch.common.util;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.NavigableMap;
import java.util.Objects;
import java.util.Set;
import java.util.TreeMap;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collector;
import java.util.stream.Collectors;
public class Maps {
/**
* Adds an entry to an immutable map by copying the underlying map and adding the new entry. This method expects there is not already a
* mapping for the specified key in the map.
*
* @param map the immutable map to concatenate the entry to
* @param key the key of the new entry
* @param value the value of the entry
* @param the type of the keys in the map
* @param the type of the values in the map
* @return an immutable map that contains the items from the specified map and the concatenated entry
*/
@SuppressWarnings("unchecked")
public static Map copyMapWithAddedEntry(final Map map, final K key, final V value) {
assert assertIsImmutableMapAndNonNullKey(map, key, value);
assert value != null;
assert map.containsKey(key) == false : "expected entry [" + key + "] to not already be present in map";
@SuppressWarnings("rawtypes")
final Map.Entry[] entries = new Map.Entry[map.size() + 1];
map.entrySet().toArray(entries);
entries[entries.length - 1] = Map.entry(key, value);
return Map.ofEntries(entries);
}
/**
* Adds a new entry to or replaces an existing entry in an immutable map by copying the underlying map and adding the new or replacing
* the existing entry.
*
* @param map the immutable map to add to or replace in
* @param key the key of the new entry
* @param value the value of the new entry
* @param the type of the keys in the map
* @param the type of the values in the map
* @return an immutable map that contains the items from the specified map and a mapping from the specified key to the specified value
*/
@SuppressWarnings("unchecked")
public static Map copyMapWithAddedOrReplacedEntry(final Map map, final K key, final V value) {
final V existing = map.get(key);
if (existing == null) {
return copyMapWithAddedEntry(map, key, value);
}
assert assertIsImmutableMapAndNonNullKey(map, key, value);
assert value != null;
@SuppressWarnings("rawtypes")
final Map.Entry[] entries = new Map.Entry[map.size()];
boolean replaced = false;
int i = 0;
for (Map.Entry entry : map.entrySet()) {
if (replaced == false && entry.getKey().equals(key)) {
entry = Map.entry(entry.getKey(), value);
replaced = true;
}
entries[i++] = entry;
}
return Map.ofEntries(entries);
}
/**
* Remove the specified key from the provided immutable map by copying the underlying map and filtering out the specified
* key if that key exists.
*
* @param map the immutable map to remove the key from
* @param key the key to be removed
* @param the type of the keys in the map
* @param the type of the values in the map
* @return an immutable map that contains the items from the specified map with the provided key removed
*/
public static Map copyMapWithRemovedEntry(final Map map, final K key) {
assert assertIsImmutableMapAndNonNullKey(map, key, map.get(key));
return map.entrySet()
.stream()
.filter(k -> key.equals(k.getKey()) == false)
.collect(Collectors.toUnmodifiableMap(Map.Entry::getKey, Map.Entry::getValue));
}
// map classes that are known to be immutable, used to speed up immutability check in #assertImmutableMap
private static final Set> IMMUTABLE_MAP_CLASSES = Set.of(
Collections.emptyMap().getClass(),
Collections.unmodifiableMap(new HashMap<>()).getClass(),
Map.of().getClass(),
Map.of("a", "b").getClass()
);
private static boolean assertIsImmutableMapAndNonNullKey(final Map map, final K key, final V value) {
assert key != null;
// check in the known immutable classes map first, most of the time we don't need to actually do the put and throw which is slow to
// the point of visibly slowing down internal cluster tests without this short-cut
if (IMMUTABLE_MAP_CLASSES.contains(map.getClass())) {
return true;
}
try {
map.put(key, value);
return false;
} catch (final UnsupportedOperationException ignored) {}
return true;
}
/**
* A convenience method to convert a collection of map entries to a map. The primary reason this method exists is to have a single
* source file with an unchecked suppression rather than scattered at the various call sites.
*
* @param entries the entries to convert to a map
* @param the type of the keys
* @param the type of the values
* @return an immutable map containing the specified entries
*/
public static Map ofEntries(final Collection> entries) {
@SuppressWarnings("unchecked")
final Map map = Map.ofEntries(entries.toArray(Map.Entry[]::new));
return map;
}
/**
* Returns {@code true} if the two specified maps are equal to one another. Two maps are considered equal if both represent identical
* mappings where values are checked with Objects.deepEquals. The primary use case is to check if two maps with array values are equal.
*
* @param left one map to be tested for equality
* @param right the other map to be tested for equality
* @return {@code true} if the two maps are equal
*/
public static boolean deepEquals(Map left, Map right) {
if (left == right) {
return true;
}
if (left == null || right == null || left.size() != right.size()) {
return false;
}
return left.entrySet()
.stream()
.allMatch(e -> right.containsKey(e.getKey()) && Objects.deepEquals(e.getValue(), right.get(e.getKey())));
}
/**
* Returns an array where all internal maps and optionally arrays are flattened into the root map.
*
* For example the map {"foo": {"bar": 1, "baz": [2, 3]}} will become {"foo.bar": 1, "foo.baz.0": 2, "foo.baz.1": 3}. Note that if
* maps contains keys with "." or numbers it is possible that such keys will be silently overridden. For example the map
* {"foo": {"bar": 1}, "foo.bar": 2} will become {"foo.bar": 1} or {"foo.bar": 2}.
*
* @param map - input to be flattened
* @param flattenArrays - if false, arrays will be ignored
* @param ordered - if true the resulted map will be sorted
* @return
*/
public static Map flatten(Map map, boolean flattenArrays, boolean ordered) {
return flatten(map, flattenArrays, ordered, null);
}
@SuppressWarnings("unchecked")
private static Map flatten(Map map, boolean flattenArrays, boolean ordered, String parentPath) {
Map flatMap = ordered ? new TreeMap<>() : new HashMap<>();
String prefix = parentPath != null ? parentPath + "." : "";
for (Map.Entry entry : map.entrySet()) {
if (entry.getValue() instanceof Map) {
flatMap.putAll(flatten((Map) entry.getValue(), flattenArrays, ordered, prefix + entry.getKey()));
} else if (flattenArrays && entry.getValue() instanceof List) {
flatMap.putAll(flatten((List
© 2015 - 2025 Weber Informatics LLC | Privacy Policy