This is an automated email from the ASF dual-hosted git repository. ggregory pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/commons-collections.git
The following commit(s) were added to refs/heads/master by this push: new bde0d12c5 COLLECTIONS-533 Added ArrayListValuedLinkedHashMap bde0d12c5 is described below commit bde0d12c51278f1c214f170fea4f804699d92329 Author: Peter De Maeyer <peter.de.mae...@gmail.com> AuthorDate: Mon Oct 14 21:22:06 2024 +0200 COLLECTIONS-533 Added ArrayListValuedLinkedHashMap --- .../multimap/ArrayListValuedLinkedHashMap.java | 155 +++++++++++++++++++++ .../multimap/ArrayListValuedHashMapTest.java | 12 +- ....java => ArrayListValuedLinkedHashMapTest.java} | 53 +++++-- ...uedLinkedHashMap.emptyCollection.version4.5.obj | Bin 0 -> 123 bytes ...luedLinkedHashMap.fullCollection.version4.5.obj | Bin 0 -> 20893 bytes 5 files changed, 207 insertions(+), 13 deletions(-) diff --git a/src/main/java/org/apache/commons/collections4/multimap/ArrayListValuedLinkedHashMap.java b/src/main/java/org/apache/commons/collections4/multimap/ArrayListValuedLinkedHashMap.java new file mode 100644 index 000000000..3c238156f --- /dev/null +++ b/src/main/java/org/apache/commons/collections4/multimap/ArrayListValuedLinkedHashMap.java @@ -0,0 +1,155 @@ +/* + * 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.commons.collections4.multimap; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Collection; +import java.util.LinkedHashMap; +import java.util.Map; + +import org.apache.commons.collections4.MultiValuedMap; + +/** + * Implements a {@code ListValuedMap}, using a {@link LinkedHashMap} to provide data + * storage and {@link ArrayList}s as value collections. This is the standard + * implementation of a ListValuedMap. + * <p> + * <strong>Note that ArrayListValuedLinkedHashMap is not synchronized and is not + * thread-safe.</strong> If you wish to use this map from multiple threads + * concurrently, you must use appropriate synchronization. This class may throw + * exceptions when accessed by concurrent threads without synchronization. + * </p> + * + * @param <K> the type of the keys in this map + * @param <V> the type of the values in this map + * @since 4.5 + */ +public class ArrayListValuedLinkedHashMap<K, V> extends AbstractListValuedMap<K, V> + implements Serializable { + + /** Serialization Version */ + private static final long serialVersionUID = 20241014L; + + /** + * The initial map capacity used when none specified in constructor. + */ + private static final int DEFAULT_INITIAL_MAP_CAPACITY = 16; + + /** + * The initial list capacity when using none specified in constructor. + */ + private static final int DEFAULT_INITIAL_LIST_CAPACITY = 3; + + /** + * The initial list capacity when creating a new value collection. + */ + private final int initialListCapacity; + + /** + * Creates an empty ArrayListValuedHashMap with the default initial + * map capacity (16) and the default initial list capacity (3). + */ + public ArrayListValuedLinkedHashMap() { + this(DEFAULT_INITIAL_MAP_CAPACITY, DEFAULT_INITIAL_LIST_CAPACITY); + } + + /** + * Creates an empty ArrayListValuedHashMap with the default initial + * map capacity (16) and the specified initial list capacity. + * + * @param initialListCapacity the initial capacity used for value collections + */ + public ArrayListValuedLinkedHashMap(final int initialListCapacity) { + this(DEFAULT_INITIAL_MAP_CAPACITY, initialListCapacity); + } + + /** + * Creates an empty ArrayListValuedHashMap with the specified initial + * map and list capacities. + * + * @param initialMapCapacity the initial hashmap capacity + * @param initialListCapacity the initial capacity used for value collections + */ + public ArrayListValuedLinkedHashMap(final int initialMapCapacity, final int initialListCapacity) { + super(new LinkedHashMap<>(initialMapCapacity)); + this.initialListCapacity = initialListCapacity; + } + + /** + * Creates an ArrayListValuedHashMap copying all the mappings of the given map. + * + * @param map a {@code Map} to copy into this map + */ + public ArrayListValuedLinkedHashMap(final Map<? extends K, ? extends V> map) { + this(map.size(), DEFAULT_INITIAL_LIST_CAPACITY); + super.putAll(map); + } + + /** + * Creates an ArrayListValuedHashMap copying all the mappings of the given map. + * + * @param map a {@code MultiValuedMap} to copy into this map + */ + public ArrayListValuedLinkedHashMap(final MultiValuedMap<? extends K, ? extends V> map) { + this(map.size(), DEFAULT_INITIAL_LIST_CAPACITY); + super.putAll(map); + } + + @Override + protected ArrayList<V> createCollection() { + return new ArrayList<>(initialListCapacity); + } + + /** + * Deserializes an instance from an ObjectInputStream. + * + * @param in The source ObjectInputStream. + * @throws IOException Any of the usual Input/Output related exceptions. + * @throws ClassNotFoundException A class of a serialized object cannot be found. + */ + private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException { + in.defaultReadObject(); + setMap(new LinkedHashMap<>()); + doReadObject(in); + } + + /** + * Trims the capacity of all value collections to their current size. + */ + public void trimToSize() { + for (final Collection<V> coll : getMap().values()) { + final ArrayList<V> list = (ArrayList<V>) coll; + list.trimToSize(); + } + } + + /** + * Serializes this object to an ObjectOutputStream. + * + * @param out the target ObjectOutputStream. + * @throws IOException thrown when an I/O errors occur writing to the target stream. + */ + private void writeObject(final ObjectOutputStream out) throws IOException { + out.defaultWriteObject(); + doWriteObject(out); + } + +} diff --git a/src/test/java/org/apache/commons/collections4/multimap/ArrayListValuedHashMapTest.java b/src/test/java/org/apache/commons/collections4/multimap/ArrayListValuedHashMapTest.java index 89830a9d0..aed4b1414 100644 --- a/src/test/java/org/apache/commons/collections4/multimap/ArrayListValuedHashMapTest.java +++ b/src/test/java/org/apache/commons/collections4/multimap/ArrayListValuedHashMapTest.java @@ -254,11 +254,19 @@ public class ArrayListValuedHashMapTest<K, V> extends AbstractMultiValuedMapTest assertEquals("Q", list3.get(2)); } + @Test + public void testCopyConstructorWithMultiValuedMap() { + final ListValuedMap<K, V> map = makeObject(); + map.put((K) "key", (V) "sleutel"); + final ListValuedMap<K, V> copy = new ArrayListValuedHashMap<>(map); + assertEquals(map, copy); + } + // public void testCreate() throws Exception { // writeExternalFormToDisk((java.io.Serializable) makeObject(), -// "src/test/resources/data/test/ArrayListValuedHashMap.emptyCollection.version4.1.obj"); +// "src/test/resources/org/apache/commons/collections4/data/test/ArrayListValuedHashMap.emptyCollection.version4.1.obj"); // writeExternalFormToDisk((java.io.Serializable) makeFullMap(), -// "src/test/resources/data/test/ArrayListValuedHashMap.fullCollection.version4.1.obj"); +// "src/test/resources/org/apache/commons/collections4/data/test/ArrayListValuedHashMap.fullCollection.version4.1.obj"); // } } diff --git a/src/test/java/org/apache/commons/collections4/multimap/ArrayListValuedHashMapTest.java b/src/test/java/org/apache/commons/collections4/multimap/ArrayListValuedLinkedHashMapTest.java similarity index 81% copy from src/test/java/org/apache/commons/collections4/multimap/ArrayListValuedHashMapTest.java copy to src/test/java/org/apache/commons/collections4/multimap/ArrayListValuedLinkedHashMapTest.java index 89830a9d0..a05758f63 100644 --- a/src/test/java/org/apache/commons/collections4/multimap/ArrayListValuedHashMapTest.java +++ b/src/test/java/org/apache/commons/collections4/multimap/ArrayListValuedLinkedHashMapTest.java @@ -29,17 +29,18 @@ import java.util.ListIterator; import java.util.Map; import org.apache.commons.collections4.ListValuedMap; +import org.apache.commons.collections4.MapIterator; import org.apache.commons.collections4.MultiValuedMap; import org.apache.commons.collections4.collection.AbstractCollectionTest; import org.junit.jupiter.api.Test; /** - * Tests {@link ArrayListValuedHashMap}. + * Tests {@link ArrayListValuedLinkedHashMap}. */ -public class ArrayListValuedHashMapTest<K, V> extends AbstractMultiValuedMapTest<K, V> { +public class ArrayListValuedLinkedHashMapTest<K, V> extends AbstractMultiValuedMapTest<K, V> { - public ArrayListValuedHashMapTest() { - super(ArrayListValuedHashMapTest.class.getSimpleName()); + public ArrayListValuedLinkedHashMapTest() { + super(ArrayListValuedLinkedHashMapTest.class.getSimpleName()); } @Override @@ -49,11 +50,16 @@ public class ArrayListValuedHashMapTest<K, V> extends AbstractMultiValuedMapTest @Override public ListValuedMap<K, V> makeObject() { - return new ArrayListValuedHashMap<>(); + return new ArrayListValuedLinkedHashMap<>(); + } + + @Override + public String getCompatibilityVersion() { + return "4.5"; // ArrayListValuedLinkedHashMap has been added in version 4.5 } @Test - public void testArrayListValuedHashMap() { + public void testArrayListValuedLinkedHashMap() { final ListValuedMap<K, V> listMap; final ListValuedMap<K, V> listMap1; final Map<K, V> map = new HashMap<>(); @@ -62,12 +68,12 @@ public class ArrayListValuedHashMapTest<K, V> extends AbstractMultiValuedMapTest map.put((K) "B", (V) "X"); map.put((K) "C", (V) "F"); - listMap = new ArrayListValuedHashMap<>(map); + listMap = new ArrayListValuedLinkedHashMap<>(map); assertEquals(1, listMap.get((K) "A").size()); assertEquals(1, listMap.get((K) "B").size()); assertEquals(1, listMap.get((K) "C").size()); - listMap1 = new ArrayListValuedHashMap<>(map1); + listMap1 = new ArrayListValuedLinkedHashMap<>(map1); assertEquals("{}", listMap1.toString()); } @@ -177,7 +183,7 @@ public class ArrayListValuedHashMapTest<K, V> extends AbstractMultiValuedMapTest @Test public void testTrimToSize() { - final ArrayListValuedHashMap<K, V> listMap = new ArrayListValuedHashMap<>(4); + final ArrayListValuedLinkedHashMap<K, V> listMap = new ArrayListValuedLinkedHashMap<>(4); assertEquals("{}", listMap.toString()); listMap.put((K) "A", (V) "W"); @@ -254,11 +260,36 @@ public class ArrayListValuedHashMapTest<K, V> extends AbstractMultiValuedMapTest assertEquals("Q", list3.get(2)); } + @Test + public void testPreservesKeyInsertionOrder() { + final ListValuedMap<K, V> map = makeObject(); + map.put((K) Integer.valueOf(5), (V) "five"); + map.put((K) Integer.valueOf(1), (V) "one"); + map.put((K) Integer.valueOf(5), (V) "vijf"); // "vijf" = "five" in Dutch + MapIterator<K, V> mapIterator = map.mapIterator(); + assertEquals(5, mapIterator.next()); + assertEquals("five", mapIterator.getValue()); + assertEquals(5, mapIterator.next()); + assertEquals("vijf", mapIterator.getValue()); + assertEquals(1, mapIterator.next()); + assertEquals("one", mapIterator.getValue()); + assertFalse(mapIterator.hasNext()); + } + + @Test + public void testCopyConstructorWithMultiValuedMap() { + final ListValuedMap<K, V> map = makeObject(); + map.put((K) "key", (V) "sleutel"); + final ListValuedMap<K, V> copy = new ArrayListValuedLinkedHashMap<>(map); + assertEquals(map, copy); + } + +// @Test // public void testCreate() throws Exception { // writeExternalFormToDisk((java.io.Serializable) makeObject(), -// "src/test/resources/data/test/ArrayListValuedHashMap.emptyCollection.version4.1.obj"); +// "src/test/resources/org/apache/commons/collections4/data/test/ArrayListValuedLinkedHashMap.emptyCollection.version4.5.obj"); // writeExternalFormToDisk((java.io.Serializable) makeFullMap(), -// "src/test/resources/data/test/ArrayListValuedHashMap.fullCollection.version4.1.obj"); +// "src/test/resources/org/apache/commons/collections4/data/test/ArrayListValuedLinkedHashMap.fullCollection.version4.5.obj"); // } } diff --git a/src/test/resources/org/apache/commons/collections4/data/test/ArrayListValuedLinkedHashMap.emptyCollection.version4.5.obj b/src/test/resources/org/apache/commons/collections4/data/test/ArrayListValuedLinkedHashMap.emptyCollection.version4.5.obj new file mode 100644 index 000000000..1b2bd01f8 Binary files /dev/null and b/src/test/resources/org/apache/commons/collections4/data/test/ArrayListValuedLinkedHashMap.emptyCollection.version4.5.obj differ diff --git a/src/test/resources/org/apache/commons/collections4/data/test/ArrayListValuedLinkedHashMap.fullCollection.version4.5.obj b/src/test/resources/org/apache/commons/collections4/data/test/ArrayListValuedLinkedHashMap.fullCollection.version4.5.obj new file mode 100644 index 000000000..7499484e0 Binary files /dev/null and b/src/test/resources/org/apache/commons/collections4/data/test/ArrayListValuedLinkedHashMap.fullCollection.version4.5.obj differ