Resolve collisions in composite collections
Before this commit, creating a CompositeMap from two maps with the same key has strange results, such as entrySet returning duplicate entries with the same key. After this commit, we give precedence to the first map by filtering out all entries in the second map that are also mapped by the first map. See gh-32245
This commit is contained in:
parent
d5664ba01a
commit
3897ea78bb
|
@ -515,6 +515,10 @@ public abstract class CollectionUtils {
|
|||
* Return a (partially unmodifiable) map that combines the provided two
|
||||
* maps. Invoking {@link Map#put(Object, Object)} or {@link Map#putAll(Map)}
|
||||
* on the returned map results in an {@link UnsupportedOperationException}.
|
||||
*
|
||||
* <p>In the case of a key collision, {@code first} takes precedence over
|
||||
* {@code second}. In other words, entries in {@code second} with a key
|
||||
* that is also mapped by {@code first} are effectively ignored.
|
||||
* @param first the first map to compose
|
||||
* @param second the second map to compose
|
||||
* @return a new map that composes the given two maps
|
||||
|
@ -531,6 +535,10 @@ public abstract class CollectionUtils {
|
|||
* {@link UnsupportedOperationException} {@code putFunction} is
|
||||
* {@code null}. The same applies to {@link Map#putAll(Map)} and
|
||||
* {@code putAllFunction}.
|
||||
*
|
||||
* <p>In the case of a key collision, {@code first} takes precedence over
|
||||
* {@code second}. In other words, entries in {@code second} with a key
|
||||
* that is also mapped by {@code first} are effectively ignored.
|
||||
* @param first the first map to compose
|
||||
* @param second the second map to compose
|
||||
* @param putFunction applied when {@code Map::put} is invoked. If
|
||||
|
|
|
@ -58,7 +58,7 @@ final class CompositeMap<K, V> implements Map<K, V> {
|
|||
Assert.notNull(first, "First must not be null");
|
||||
Assert.notNull(second, "Second must not be null");
|
||||
this.first = first;
|
||||
this.second = second;
|
||||
this.second = new FilteredMap<>(second, key -> !this.first.containsKey(key));
|
||||
this.putFunction = putFunction;
|
||||
this.putAllFunction = putAllFunction;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,90 @@
|
|||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed 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
|
||||
*
|
||||
* https://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.springframework.util;
|
||||
|
||||
import java.util.AbstractCollection;
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
/**
|
||||
* Collection that filters out values that do not match a predicate.
|
||||
* This type is used by {@link CompositeMap}.
|
||||
* @author Arjen Poutsma
|
||||
* @since 6.2
|
||||
* @param <E> the type of elements maintained by this collection
|
||||
*/
|
||||
class FilteredCollection<E> extends AbstractCollection<E> {
|
||||
|
||||
private final Collection<E> delegate;
|
||||
|
||||
private final Predicate<E> filter;
|
||||
|
||||
|
||||
public FilteredCollection(Collection<E> delegate, Predicate<E> filter) {
|
||||
Assert.notNull(delegate, "Delegate must not be null");
|
||||
Assert.notNull(filter, "Filter must not be null");
|
||||
|
||||
this.delegate = delegate;
|
||||
this.filter = filter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
int size = 0;
|
||||
for (E e : this.delegate) {
|
||||
if (this.filter.test(e)) {
|
||||
size++;
|
||||
}
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<E> iterator() {
|
||||
return new FilteredIterator<>(this.delegate.iterator(), this.filter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean add(E e) {
|
||||
boolean added = this.delegate.add(e);
|
||||
return added && this.filter.test(e);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public boolean remove(Object o) {
|
||||
boolean removed = this.delegate.remove(o);
|
||||
return removed && this.filter.test((E) o);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public boolean contains(Object o) {
|
||||
if (this.delegate.contains(o)) {
|
||||
return this.filter.test((E) o);
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
this.delegate.clear();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,86 @@
|
|||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed 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
|
||||
*
|
||||
* https://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.springframework.util;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* Iterator that filters out values that do not match a predicate.
|
||||
* This type is used by {@link CompositeMap}.
|
||||
* @author Arjen Poutsma
|
||||
* @since 6.2
|
||||
* @param <E> the type of elements returned by this iterator
|
||||
*/
|
||||
final class FilteredIterator<E> implements Iterator<E> {
|
||||
|
||||
private final Iterator<E> delegate;
|
||||
|
||||
private final Predicate<E> filter;
|
||||
|
||||
@Nullable
|
||||
private E next;
|
||||
|
||||
private boolean nextSet;
|
||||
|
||||
|
||||
public FilteredIterator(Iterator<E> delegate, Predicate<E> filter) {
|
||||
Assert.notNull(delegate, "Delegate must not be null");
|
||||
Assert.notNull(filter, "Filter must not be null");
|
||||
|
||||
this.delegate = delegate;
|
||||
this.filter = filter;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
if (this.nextSet) {
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
return setNext();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public E next() {
|
||||
if (!this.nextSet) {
|
||||
if (!setNext()) {
|
||||
throw new NoSuchElementException();
|
||||
}
|
||||
}
|
||||
this.nextSet = false;
|
||||
Assert.state(this.next != null, "Next should not be null");
|
||||
return this.next;
|
||||
}
|
||||
|
||||
private boolean setNext() {
|
||||
while (this.delegate.hasNext()) {
|
||||
E next = this.delegate.next();
|
||||
if (this.filter.test(next)) {
|
||||
this.next = next;
|
||||
this.nextSet = true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,124 @@
|
|||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed 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
|
||||
*
|
||||
* https://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.springframework.util;
|
||||
|
||||
import java.util.AbstractMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* Map that filters out values that do not match a predicate.
|
||||
* This type is used by {@link CompositeMap}.
|
||||
* @author Arjen Poutsma
|
||||
* @since 6.2
|
||||
* @param <K> the type of keys maintained by this map
|
||||
* @param <V> the type of mapped values
|
||||
*/
|
||||
final class FilteredMap<K, V> extends AbstractMap<K, V> {
|
||||
|
||||
private final Map<K, V> delegate;
|
||||
|
||||
private final Predicate<K> filter;
|
||||
|
||||
|
||||
public FilteredMap(Map<K, V> delegate, Predicate<K> filter) {
|
||||
Assert.notNull(delegate, "Delegate must not be null");
|
||||
Assert.notNull(filter, "Filter must not be null");
|
||||
|
||||
this.delegate = delegate;
|
||||
this.filter = filter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Entry<K, V>> entrySet() {
|
||||
return new FilteredSet<>(this.delegate.entrySet(), entry -> this.filter.test(entry.getKey()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
int size = 0;
|
||||
for (K k : keySet()) {
|
||||
if (this.filter.test(k)) {
|
||||
size++;
|
||||
}
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public boolean containsKey(Object key) {
|
||||
if (this.delegate.containsKey(key)) {
|
||||
return this.filter.test((K) key);
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
@Nullable
|
||||
public V get(Object key) {
|
||||
V value = this.delegate.get(key);
|
||||
if (value != null && this.filter.test((K) key)) {
|
||||
return value;
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public V put(K key, V value) {
|
||||
V oldValue = this.delegate.put(key, value);
|
||||
if (oldValue != null && this.filter.test(key)) {
|
||||
return oldValue;
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
@Nullable
|
||||
public V remove(Object key) {
|
||||
V oldValue = this.delegate.remove(key);
|
||||
if (oldValue != null && this.filter.test((K) key)) {
|
||||
return oldValue;
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
this.delegate.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<K> keySet() {
|
||||
return new FilteredSet<>(this.delegate.keySet(), this.filter);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,66 @@
|
|||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed 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
|
||||
*
|
||||
* https://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.springframework.util;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
/**
|
||||
* Set that filters out values that do not match a predicate.
|
||||
* This type is used by {@link CompositeMap}.
|
||||
* @author Arjen Poutsma
|
||||
* @since 6.2
|
||||
* @param <E> the type of elements maintained by this set
|
||||
*/
|
||||
final class FilteredSet<E> extends FilteredCollection<E> implements Set<E> {
|
||||
|
||||
public FilteredSet(Set<E> delegate, Predicate<E> filter) {
|
||||
super(delegate, filter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == this) {
|
||||
return true;
|
||||
}
|
||||
else if (obj instanceof Set<?> set) {
|
||||
if (set.size() != size()) {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
return containsAll(set);
|
||||
}
|
||||
catch (ClassCastException | NullPointerException ignored) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int hashCode = 0;
|
||||
for (E obj : this) {
|
||||
if (obj != null) {
|
||||
hashCode += obj.hashCode();
|
||||
}
|
||||
}
|
||||
return hashCode;
|
||||
}
|
||||
}
|
|
@ -23,6 +23,7 @@ import java.util.Map;
|
|||
import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import org.junit.jupiter.api.Nested;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
@ -218,4 +219,85 @@ class CompositeMapTests {
|
|||
Set<Map.Entry<String, String>> entries = composite.entrySet();
|
||||
assertThat(entries).containsExactly(entry("foo", "bar"), entry("baz", "qux"));
|
||||
}
|
||||
|
||||
@Nested
|
||||
class CollisionTests {
|
||||
|
||||
@Test
|
||||
void size() {
|
||||
Map<String, String> first = Map.of("foo", "bar", "baz", "qux");
|
||||
Map<String, String> second = Map.of("baz", "quux", "corge", "grault");
|
||||
CompositeMap<String, String> composite = new CompositeMap<>(first, second);
|
||||
|
||||
assertThat(composite).hasSize(3);
|
||||
}
|
||||
|
||||
@Test
|
||||
void containsValue() {
|
||||
Map<String, String> first = Map.of("foo", "bar", "baz", "qux");
|
||||
Map<String, String> second = Map.of("baz", "quux", "corge", "grault");
|
||||
CompositeMap<String, String> composite = new CompositeMap<>(first, second);
|
||||
|
||||
assertThat(composite.containsValue("bar")).isTrue();
|
||||
assertThat(composite.containsValue("qux")).isTrue();
|
||||
assertThat(composite.containsValue("quux")).isFalse();
|
||||
assertThat(composite.containsValue("grault")).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void get() {
|
||||
Map<String, String> first = Map.of("foo", "bar", "baz", "qux");
|
||||
Map<String, String> second = Map.of("baz", "quux", "corge", "grault");
|
||||
CompositeMap<String, String> composite = new CompositeMap<>(first, second);
|
||||
|
||||
assertThat(composite.get("foo")).isEqualTo("bar");
|
||||
assertThat(composite.get("baz")).isEqualTo("qux");
|
||||
assertThat(composite.get("corge")).isEqualTo("grault");
|
||||
}
|
||||
|
||||
@Test
|
||||
void remove() {
|
||||
Map<String, String> first = new HashMap<>(Map.of("foo", "bar", "baz", "qux"));
|
||||
Map<String, String> second = new HashMap<>(Map.of("baz", "quux", "corge", "grault"));
|
||||
CompositeMap<String, String> composite = new CompositeMap<>(first, second);
|
||||
|
||||
assertThat(composite.remove("baz")).isEqualTo("qux");
|
||||
assertThat(composite.containsKey("baz")).isFalse();
|
||||
assertThat(first).containsExactly(entry("foo", "bar"));
|
||||
assertThat(second).containsExactly(entry("corge", "grault"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void keySet() {
|
||||
Map<String, String> first = Map.of("foo", "bar", "baz", "qux");
|
||||
Map<String, String> second = Map.of("baz", "quux", "corge", "grault");
|
||||
CompositeMap<String, String> composite = new CompositeMap<>(first, second);
|
||||
|
||||
Set<String> keySet = composite.keySet();
|
||||
assertThat(keySet).containsExactlyInAnyOrder("foo", "baz", "corge");
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
void values() {
|
||||
Map<String, String> first = Map.of("foo", "bar", "baz", "qux");
|
||||
Map<String, String> second = Map.of("baz", "quux", "corge", "grault");
|
||||
CompositeMap<String, String> composite = new CompositeMap<>(first, second);
|
||||
|
||||
Collection<String> values = composite.values();
|
||||
assertThat(values).containsExactlyInAnyOrder("bar", "qux", "grault");
|
||||
}
|
||||
|
||||
@Test
|
||||
void entrySet() {
|
||||
Map<String, String> first = Map.of("foo", "bar", "baz", "qux");
|
||||
Map<String, String> second = Map.of("baz", "quux", "corge", "grault");
|
||||
CompositeMap<String, String> composite = new CompositeMap<>(first, second);
|
||||
|
||||
Set<Map.Entry<String, String>> entries = composite.entrySet();
|
||||
assertThat(entries).containsExactlyInAnyOrder(entry("foo", "bar"), entry("baz", "qux"), entry("corge", "grault"));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,77 @@
|
|||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed 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
|
||||
*
|
||||
* https://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.springframework.util;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* @author Arjen Poutsma
|
||||
*/
|
||||
class FilteredCollectionTests {
|
||||
|
||||
@Test
|
||||
void size() {
|
||||
List<String> list = List.of("foo", "bar", "baz");
|
||||
FilteredCollection<String> filtered = new FilteredCollection<>(list, s -> !s.equals("bar"));
|
||||
|
||||
assertThat(filtered).hasSize(2);
|
||||
}
|
||||
|
||||
@Test
|
||||
void iterator() {
|
||||
List<String> list = List.of("foo", "bar", "baz");
|
||||
FilteredCollection<String> filtered = new FilteredCollection<>(list, s -> !s.equals("bar"));
|
||||
|
||||
assertThat(filtered.iterator()).toIterable().containsExactly("foo", "baz");
|
||||
}
|
||||
|
||||
@Test
|
||||
void add() {
|
||||
List<String> list = new ArrayList<>(List.of("foo"));
|
||||
FilteredCollection<String> filtered = new FilteredCollection<>(list, s -> !s.equals("bar"));
|
||||
boolean added = filtered.add("bar");
|
||||
assertThat(added).isFalse();
|
||||
assertThat(filtered).containsExactly("foo");
|
||||
assertThat(list).containsExactly("foo", "bar");
|
||||
}
|
||||
|
||||
@Test
|
||||
void remove() {
|
||||
List<String> list = new ArrayList<>(List.of("foo", "bar"));
|
||||
FilteredCollection<String> filtered = new FilteredCollection<>(list, s -> !s.equals("bar"));
|
||||
assertThat(list).contains("bar");
|
||||
assertThat(filtered).doesNotContain("bar");
|
||||
boolean removed = filtered.remove("bar");
|
||||
assertThat(removed).isFalse();
|
||||
assertThat(filtered).doesNotContain("bar");
|
||||
assertThat(list).doesNotContain("bar");
|
||||
}
|
||||
|
||||
@Test
|
||||
void contains() {
|
||||
List<String> list = List.of("foo", "bar", "baz");
|
||||
FilteredCollection<String> filtered = new FilteredCollection<>(list, s -> !s.equals("bar"));
|
||||
boolean contained = filtered.contains("bar");
|
||||
assertThat(contained).isFalse();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed 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
|
||||
*
|
||||
* https://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.springframework.util;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* @author Arjen Poutsma
|
||||
*/
|
||||
final class FilteredIteratorTests {
|
||||
|
||||
@Test
|
||||
void filter() {
|
||||
List<String> list = List.of("foo", "bar", "baz");
|
||||
FilteredIterator<String> filtered = new FilteredIterator<>(list.iterator(), s -> !s.equals("bar"));
|
||||
|
||||
assertThat(filtered).toIterable().containsExactly("foo", "baz");
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,103 @@
|
|||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed 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
|
||||
*
|
||||
* https://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.springframework.util;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static java.util.Map.entry;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* @author Arjen Poutsma
|
||||
*/
|
||||
class FilteredMapTests {
|
||||
|
||||
@Test
|
||||
void size() {
|
||||
Map<String, String> map = Map.of("foo", "bar", "baz", "qux", "quux", "corge");
|
||||
FilteredMap<String, String> filtered = new FilteredMap<>(map, s -> !s.equals("baz"));
|
||||
|
||||
assertThat(filtered).hasSize(2);
|
||||
}
|
||||
|
||||
@Test
|
||||
void entrySet() {
|
||||
Map<String, String> map = Map.of("foo", "bar", "baz", "qux", "quux", "corge");
|
||||
FilteredMap<String, String> filtered = new FilteredMap<>(map, s -> !s.equals("baz"));
|
||||
|
||||
assertThat(filtered.entrySet()).containsExactlyInAnyOrder(entry("foo", "bar"), entry("quux", "corge"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void containsKey() {
|
||||
Map<String, String> map = Map.of("foo", "bar", "baz", "qux", "quux", "corge");
|
||||
FilteredMap<String, String> filtered = new FilteredMap<>(map, s -> !s.equals("baz"));
|
||||
|
||||
boolean contained = filtered.containsKey("baz");
|
||||
assertThat(contained).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void get() {
|
||||
Map<String, String> map = Map.of("foo", "bar", "baz", "qux", "quux", "corge");
|
||||
FilteredMap<String, String> filtered = new FilteredMap<>(map, s -> !s.equals("baz"));
|
||||
|
||||
String value = filtered.get("baz");
|
||||
assertThat(value).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void put() {
|
||||
Map<String, String> map = new HashMap<>(Map.of("foo", "bar", "quux", "corge"));
|
||||
FilteredMap<String, String> filtered = new FilteredMap<>(map, s -> !s.equals("baz"));
|
||||
|
||||
String value = filtered.put("baz", "qux");
|
||||
assertThat(value).isNull();
|
||||
assertThat(filtered.containsKey("baz")).isFalse();
|
||||
assertThat(map.get("baz")).isEqualTo("qux");
|
||||
|
||||
// overwrite
|
||||
value = filtered.put("baz", "QUX");
|
||||
assertThat(value).isNull();
|
||||
assertThat(filtered.containsKey("baz")).isFalse();
|
||||
assertThat(map.get("baz")).isEqualTo("QUX");
|
||||
}
|
||||
|
||||
@Test
|
||||
void remove() {
|
||||
Map<String, String> map = new HashMap<>(Map.of("foo", "bar", "baz", "qux", "quux", "corge"));
|
||||
FilteredMap<String, String> filtered = new FilteredMap<>(map, s -> !s.equals("baz"));
|
||||
|
||||
String value = filtered.remove("baz");
|
||||
assertThat(value).isNull();
|
||||
assertThat(filtered.containsKey("baz")).isFalse();
|
||||
assertThat(map.containsKey("baz")).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void keySet() {
|
||||
Map<String, String> map = Map.of("foo", "bar", "baz", "qux", "quux", "corge");
|
||||
FilteredMap<String, String> filtered = new FilteredMap<>(map, s -> !s.equals("baz"));
|
||||
|
||||
Set<String> keySet = filtered.keySet();
|
||||
assertThat(keySet).containsExactlyInAnyOrder("foo", "quux");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
*
|
||||
* Licensed 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
|
||||
*
|
||||
* https://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.springframework.util;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* @author Arjen Poutsma
|
||||
*/
|
||||
class FilteredSetTests {
|
||||
|
||||
@Test
|
||||
void testEquals() {
|
||||
Set<String> set = Set.of("foo", "bar", "baz");
|
||||
FilteredSet<String> filtered = new FilteredSet<>(set, s -> !s.equals("bar"));
|
||||
|
||||
Set<String> expected = Set.of("foo", "baz");
|
||||
|
||||
assertThat(filtered.equals(expected)).isTrue();
|
||||
assertThat(filtered.equals(set)).isFalse();
|
||||
assertThat(filtered.equals(Collections.emptySet())).isFalse();
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue