diff --git a/hutool-core/src/main/java/cn/hutool/core/map/TolerantMap.java b/hutool-core/src/main/java/cn/hutool/core/map/TolerantMap.java new file mode 100644 index 000000000..580f39599 --- /dev/null +++ b/hutool-core/src/main/java/cn/hutool/core/map/TolerantMap.java @@ -0,0 +1,203 @@ +package cn.hutool.core.map; + +import cn.hutool.core.util.ObjectUtil; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; +import java.util.*; +import java.util.function.BiConsumer; +import java.util.function.BiFunction; +import java.util.function.Function; + +/** + * 一个可以提供默认值的Map + * + * @author pantao + * @since 2020/1/3 + */ +public class TolerantMap extends AbstractMap implements Map, Cloneable, Serializable { + + private static final long serialVersionUID = -4158133823263496197L; + + private transient Map map; + + private transient V defaultValue; + + public TolerantMap(V defaultValue) { + this(new HashMap<>(), defaultValue); + } + + public TolerantMap(int initialCapacity, float loadFactor, V defaultValue) { + this(new HashMap<>(initialCapacity, loadFactor), defaultValue); + } + + public TolerantMap(int initialCapacity, V defaultValue) { + this(new HashMap<>(initialCapacity), defaultValue); + } + + public TolerantMap(Map map, V defaultValue) { + this.map = map; + this.defaultValue = defaultValue; + } + + public static TolerantMap of(Map map, V defaultValue) { + return new TolerantMap<>(map, defaultValue); + } + + @Override + public int size() { + return map.size(); + } + + @Override + public boolean isEmpty() { + return map.isEmpty(); + } + + @Override + public boolean containsValue(Object value) { + return map.containsValue(value); + } + + @Override + public boolean containsKey(Object key) { + return map.containsKey(key); + } + + @Override + public V get(Object key) { + return getOrDefault(key, defaultValue); + } + + @Override + public V put(K key, V value) { + return map.put(key, value); + } + + @Override + public V remove(Object key) { + return map.remove(key); + } + + @Override + public void putAll(Map m) { + map.putAll(m); + } + + @Override + public void clear() { + map.clear(); + } + + @Override + public Set keySet() { + return map.keySet(); + } + + @Override + public Collection values() { + return map.values(); + } + + @Override + public Set> entrySet() { + return map.entrySet(); + } + + @Override + public V getOrDefault(Object key, V defaultValue) { + return map.getOrDefault(key, defaultValue); + } + + @Override + public void forEach(BiConsumer action) { + map.forEach(action); + } + + @Override + public void replaceAll(BiFunction function) { + map.replaceAll(function); + } + + @Override + public V putIfAbsent(K key, V value) { + return map.putIfAbsent(key, value); + } + + @Override + public boolean remove(Object key, Object value) { + return map.remove(key, value); + } + + @Override + public boolean replace(K key, V oldValue, V newValue) { + return map.replace(key, oldValue, newValue); + } + + @Override + public V replace(K key, V value) { + return map.replace(key, value); + } + + @Override + public V computeIfAbsent(K key, Function mappingFunction) { + return map.computeIfAbsent(key, mappingFunction); + } + + @Override + public V computeIfPresent(K key, BiFunction remappingFunction) { + return map.computeIfPresent(key, remappingFunction); + } + + @Override + public V compute(K key, BiFunction remappingFunction) { + return map.compute(key, remappingFunction); + } + + @Override + public V merge(K key, V value, BiFunction remappingFunction) { + return map.merge(key, value, remappingFunction); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + if (!super.equals(o)) { + return false; + } + TolerantMap that = (TolerantMap) o; + return map.equals(that.map) && Objects.equals(defaultValue, that.defaultValue); + } + + @Override + public int hashCode() { + return Objects.hash(map, defaultValue); + } + + @Override + public String toString() { + return "TolerantMap{" + "map=" + map + ", defaultValue=" + defaultValue + '}'; + } + + private void writeObject(ObjectOutputStream s) throws IOException { + s.writeObject(ObjectUtil.serialize(map)); + s.writeObject(ObjectUtil.serialize(defaultValue)); + } + + private void readObject(ObjectInputStream s) throws IOException, ClassNotFoundException { + map = ObjectUtil.deserialize((byte[]) s.readObject()); + defaultValue = ObjectUtil.deserialize((byte[]) s.readObject()); + } + + @Override + protected Object clone() throws CloneNotSupportedException { + return super.clone(); + } +} diff --git a/hutool-core/src/test/java/cn/hutool/core/map/TolerantMapTest.java b/hutool-core/src/test/java/cn/hutool/core/map/TolerantMapTest.java new file mode 100644 index 000000000..dba466118 --- /dev/null +++ b/hutool-core/src/test/java/cn/hutool/core/map/TolerantMapTest.java @@ -0,0 +1,40 @@ +package cn.hutool.core.map; + +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.RandomUtil; +import org.junit.Before; +import org.junit.Test; + +import java.util.HashMap; + +public class TolerantMapTest { + + private final TolerantMap map = TolerantMap.of(new HashMap<>(), "default"); + + @Before + public void before() { + map.put("monday", "星期一"); + map.put("tuesday", "星期二"); + } + + @Test + public void testSerialize() { + byte[] bytes = ObjectUtil.serialize(map); + TolerantMap serializedMap = ObjectUtil.deserialize(bytes); + assert serializedMap != map; + assert map.equals(serializedMap); + } + + @Test + public void testClone() { + TolerantMap clonedMap = ObjectUtil.clone(map); + assert clonedMap != map; + assert map.equals(clonedMap); + } + + @Test + public void testGet() { + assert "星期二".equals(map.get("tuesday")); + assert "default".equals(map.get(RandomUtil.randomString(6))); + } +}