summaryrefslogtreecommitdiff
path: root/src/main/java/bjc/esodata/TSetMultimap.java
diff options
context:
space:
mode:
authorBen Culkin <scorpress@gmail.com>2022-09-27 19:09:11 -0400
committerBen Culkin <scorpress@gmail.com>2022-09-27 19:09:11 -0400
commit2442e05e638a61dd1bfbd6b95cb3544b6a327af9 (patch)
tree7d332d1b84bc9338d38664de64c166f1641f3fe9 /src/main/java/bjc/esodata/TSetMultimap.java
parent2d5c3288134f19088941c980e852521e9838db56 (diff)
GPLize project
Finally deciding to move things into a proper license; which I can do since I've never accepted (or had :( ) a pull request from another person
Diffstat (limited to 'src/main/java/bjc/esodata/TSetMultimap.java')
-rw-r--r--src/main/java/bjc/esodata/TSetMultimap.java124
1 files changed, 124 insertions, 0 deletions
diff --git a/src/main/java/bjc/esodata/TSetMultimap.java b/src/main/java/bjc/esodata/TSetMultimap.java
new file mode 100644
index 0000000..1a04a41
--- /dev/null
+++ b/src/main/java/bjc/esodata/TSetMultimap.java
@@ -0,0 +1,124 @@
+/*
+ * esodata - data structures and other things, of varying utility
+ * Copyright 2022, Ben Culkin
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+package bjc.esodata;
+
+import java.util.*;
+import java.util.Map.Entry;
+
+import bjc.data.Pair;
+
+/**
+ * A map that has support for multiple values for a given key.
+ *
+ * Whenever you give another value for a key, that is then returned for that
+ * key. About the only somewhat complex thing, is that, if you add the same
+ * key-value pair multiple times, it will only show up once. However, you will
+ * have to remove that pair as many times as you added it.
+ *
+ * @author Ben Culkin
+ *
+ * @param <KeyType> The type of keys in the map.
+ * @param <ValueType> The type of values in the map.
+ */
+public class TSetMultimap<KeyType, ValueType> implements Iterable<Pair<KeyType, ValueType>>, Multimap<KeyType, ValueType> {
+ private final class SetMultimapIterator implements Iterator<Pair<KeyType, ValueType>> {
+ private Iterator<Entry<KeyType, ThresholdSet<ValueType>>> mapIter = backing.entrySet().iterator();
+ private KeyType currKey;
+ private Iterator<ValueType> setIter;
+
+ @Override
+ public boolean hasNext() {
+ while (setIter == null || !setIter.hasNext()) {
+ if (!mapIter.hasNext()) return false;
+ Entry<KeyType,ThresholdSet<ValueType>> entry = mapIter.next();
+
+ currKey = entry.getKey();
+ setIter = entry.getValue().setView().iterator();
+ }
+
+ return setIter.hasNext();
+ }
+
+ @Override
+ public Pair<KeyType, ValueType> next() {
+ if (setIter == null || !setIter.hasNext()) throw new NoSuchElementException();
+ return Pair.pair(currKey, setIter.next()) ;
+ }
+ }
+
+ private Map<KeyType, ThresholdSet<ValueType>> backing;
+
+ /**
+ * Create a new empty multimap.
+ */
+ public TSetMultimap() {
+ backing = new HashMap<>();
+ }
+
+ @Override
+ public void add(KeyType key, ValueType value) {
+ ThresholdSet<ValueType> container
+ = backing.computeIfAbsent(key, k -> new ThresholdSet<>());
+
+ container.add(value);
+ }
+
+ @Override
+ public void remove(KeyType key, ValueType value) {
+ // We have no values for that key; bail.
+ if (!backing.containsKey(key)) return;
+
+ backing.get(key).remove(value);
+ }
+
+ @Override
+ public void remove(KeyType key) {
+ backing.remove(key);
+ }
+
+ @Override
+ public Set<ValueType> get(KeyType key) {
+ if (!backing.containsKey(key)) return new HashSet<>();
+ else return backing.get(key).values();
+ }
+
+ @Override
+ public Optional<ValueType> getSingle(KeyType key) {
+ Set<ValueType> set = get(key);
+
+ if (set.size() == 1) return Optional.of(set.iterator().next());
+ return Optional.empty();
+ }
+
+ @Override
+ public boolean contains(KeyType key) {
+ return backing.containsKey(key);
+ }
+
+ @Override
+ public boolean contains(KeyType key, ValueType value) {
+ if (!backing.containsKey(key)) return false;
+
+ return backing.get(key).contains(value) > 0;
+ }
+
+ @Override
+ public Iterator<Pair<KeyType, ValueType>> iterator() {
+ return new SetMultimapIterator();
+ }
+}