1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * https://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17 package org.apache.commons.collections4.multimap;
18
19 import java.io.IOException;
20 import java.io.ObjectInputStream;
21 import java.io.ObjectOutputStream;
22 import java.io.Serializable;
23 import java.util.ArrayList;
24 import java.util.Collection;
25 import java.util.HashMap;
26 import java.util.Map;
27
28 import org.apache.commons.collections4.MultiMapUtils;
29 import org.apache.commons.collections4.MultiValuedMap;
30
31 /**
32 * Implements a {@code ListValuedMap}, using a {@link HashMap} to provide data
33 * storage and {@link ArrayList}s as value collections. This is the standard
34 * implementation of a ListValuedMap.
35 * <p>
36 * <strong>Note that ArrayListValuedHashMap is not synchronized and is not
37 * thread-safe.</strong> If you wish to use this map from multiple threads
38 * concurrently, you must use appropriate synchronization. This class may throw
39 * exceptions when accessed by concurrent threads without synchronization.
40 * </p>
41 *
42 * @param <K> The type of the keys in this map
43 * @param <V> The type of the values in this map
44 * @since 4.1
45 */
46 public class ArrayListValuedHashMap<K, V> extends AbstractListValuedMap<K, V>
47 implements Serializable {
48
49 /** Serialization Version */
50 private static final long serialVersionUID = 20151118L;
51
52 /**
53 * The initial map capacity used when none specified in constructor.
54 */
55 private static final int DEFAULT_INITIAL_MAP_CAPACITY = 16;
56
57 /**
58 * The initial list capacity when using none specified in constructor.
59 */
60 private static final int DEFAULT_INITIAL_LIST_CAPACITY = 3;
61
62 /**
63 * The initial list capacity when creating a new value collection.
64 */
65 private final int initialListCapacity;
66
67 /**
68 * Creates an empty ArrayListValuedHashMap with the default initial
69 * map capacity (16) and the default initial list capacity (3).
70 */
71 public ArrayListValuedHashMap() {
72 this(DEFAULT_INITIAL_MAP_CAPACITY, DEFAULT_INITIAL_LIST_CAPACITY);
73 }
74
75 /**
76 * Creates an empty ArrayListValuedHashMap with the default initial
77 * map capacity (16) and the specified initial list capacity.
78 *
79 * @param initialListCapacity The initial capacity used for value collections
80 */
81 public ArrayListValuedHashMap(final int initialListCapacity) {
82 this(DEFAULT_INITIAL_MAP_CAPACITY, initialListCapacity);
83 }
84
85 /**
86 * Creates an empty ArrayListValuedHashMap with the specified initial
87 * map and list capacities.
88 *
89 * @param initialMapCapacity The initial hashmap capacity
90 * @param initialListCapacity The initial capacity used for value collections
91 */
92 public ArrayListValuedHashMap(final int initialMapCapacity, final int initialListCapacity) {
93 super(new HashMap<>(initialMapCapacity));
94 this.initialListCapacity = initialListCapacity;
95 }
96
97 /**
98 * Creates an ArrayListValuedHashMap copying all the mappings of the given map.
99 *
100 * @param map A {@code Map} to copy into this map
101 */
102 public ArrayListValuedHashMap(final Map<? extends K, ? extends V> map) {
103 this(map.size(), DEFAULT_INITIAL_LIST_CAPACITY);
104 super.putAll(map);
105 }
106
107 /**
108 * Creates an ArrayListValuedHashMap copying all the mappings of the given map.
109 *
110 * @param map A {@code MultiValuedMap} to copy into this map
111 */
112 public ArrayListValuedHashMap(final MultiValuedMap<? extends K, ? extends V> map) {
113 this(map.size(), DEFAULT_INITIAL_LIST_CAPACITY);
114 super.putAll(map);
115 }
116
117 @Override
118 protected ArrayList<V> createCollection() {
119 return new ArrayList<>(initialListCapacity);
120 }
121
122 @Override
123 public ArrayListValuedHashMap<V, K> inverted() {
124 return MultiMapUtils.invert(this, new ArrayListValuedHashMap<V, K>());
125 }
126
127 /**
128 * Deserializes an instance from an ObjectInputStream.
129 *
130 * @param in The source ObjectInputStream.
131 * @throws IOException Any of the usual Input/Output related exceptions.
132 * @throws ClassNotFoundException A class of a serialized object cannot be found.
133 */
134 private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException {
135 in.defaultReadObject();
136 setMap(new HashMap<>());
137 doReadObject(in);
138 }
139
140 /**
141 * Trims the capacity of all value collections to their current size.
142 */
143 public void trimToSize() {
144 for (final Collection<V> coll : getMap().values()) {
145 final ArrayList<V> list = (ArrayList<V>) coll;
146 list.trimToSize();
147 }
148 }
149
150 /**
151 * Serializes this object to an ObjectOutputStream.
152 *
153 * @param out The target ObjectOutputStream.
154 * @throws IOException thrown when an I/O errors occur writing to the target stream.
155 */
156 private void writeObject(final ObjectOutputStream out) throws IOException {
157 out.defaultWriteObject();
158 doWriteObject(out);
159 }
160
161 }