summaryrefslogtreecommitdiffstats
path: root/mobile/android/geckoview/src/main/java/org/mozilla/gecko/MultiMap.java
blob: ff26d99dea7f62ecbb6a1d15de01fcd00d649e1f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

package org.mozilla.gecko;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * Defines a map that holds a collection of values against each key.
 *
 * @param <K> Key type
 * @param <T> Value type
 */
public class MultiMap<K, T> {
  private HashMap<K, List<T>> mMap;
  private final List<T> mEmptyList = Collections.unmodifiableList(new ArrayList<>());

  /**
   * Creates a MultiMap with specified initial capacity.
   *
   * @param count Initial capacity
   */
  public MultiMap(final int count) {
    mMap = new HashMap<>(count);
  }

  /** Creates a MultiMap with default initial capacity. */
  public MultiMap() {
    mMap = new HashMap<>();
  }

  private void ensure(final K key) {
    if (!mMap.containsKey(key)) {
      mMap.put(key, new ArrayList<>());
    }
  }

  /**
   * @return A map of key to the list of values associated to it
   */
  public Map<K, List<T>> asMap() {
    return mMap;
  }

  /**
   * @return The number of keys present in this map
   */
  public int size() {
    return mMap.size();
  }

  /**
   * @return whether this map is empty or not
   */
  public boolean isEmpty() {
    return mMap.isEmpty();
  }

  /**
   * Checks if a key is present in this map.
   *
   * @param key the key to check
   * @return True if the map contains this key, false otherwise.
   */
  public boolean containsKey(final @Nullable K key) {
    return mMap.containsKey(key);
  }

  /**
   * Checks if a (key, value) pair is present in this map.
   *
   * @param key the key to check
   * @param value the value to check
   * @return true if there is a value associated to the given key, false otherwise
   */
  public boolean containsEntry(final @Nullable K key, final @Nullable T value) {
    if (!mMap.containsKey(key)) {
      return false;
    }

    return mMap.get(key).contains(value);
  }

  /**
   * Gets the values associated with the given key.
   *
   * @param key the key to check
   * @return the list of values associated with keys, an empty list if no values are associated with
   *     key.
   */
  @NonNull
  public List<T> get(final @Nullable K key) {
    if (!mMap.containsKey(key)) {
      return mEmptyList;
    }

    return mMap.get(key);
  }

  /**
   * Add a (key, value) mapping to this map.
   *
   * @param key the key to add
   * @param value the value to add
   */
  @Nullable
  public void add(final @NonNull K key, final @NonNull T value) {
    ensure(key);
    mMap.get(key).add(value);
  }

  /**
   * Add a list of values to the given key.
   *
   * @param key the key to add
   * @param values the list of values to add
   * @return the final list of values or null if no value was added
   */
  @Nullable
  public List<T> addAll(final @NonNull K key, final @NonNull List<T> values) {
    if (values == null || values.isEmpty()) {
      return null;
    }

    ensure(key);

    final List<T> result = mMap.get(key);
    result.addAll(values);
    return result;
  }

  /**
   * Remove all mappings for the given key.
   *
   * @param key the key
   * @return values associated with the key or null if no values was present.
   */
  @Nullable
  public List<T> remove(final @Nullable K key) {
    return mMap.remove(key);
  }

  /**
   * Remove a (key, value) mapping from this map
   *
   * @param key the key to remove
   * @param value the value to remove
   * @return true if the (key, value) mapping was present, false otherwise
   */
  @Nullable
  public boolean remove(final @Nullable K key, final @Nullable T value) {
    if (!mMap.containsKey(key)) {
      return false;
    }

    final List<T> values = mMap.get(key);
    final boolean wasPresent = values.remove(value);

    if (values.isEmpty()) {
      mMap.remove(key);
    }

    return wasPresent;
  }

  /** Remove all mappings from this map. */
  public void clear() {
    mMap.clear();
  }

  /**
   * @return a set with all the keys for this map.
   */
  @NonNull
  public Set<K> keySet() {
    return mMap.keySet();
  }
}