blob: fcf28ac548dfca82045e57105f92ea35d782cccc [file] [log] [blame]
Stuart McCullochbb014372012-06-07 21:57:32 +00001package aQute.lib.collections;
2
3import java.util.*;
4
5public class MultiMap<K,V> extends HashMap<K,List<V>> {
6 private static final long serialVersionUID = 1L;
7 final boolean noduplicates;
8 final Class<?> keyClass;
9 final Class<?> valueClass;
10
11 final Set<V> EMPTY = Collections.emptySet();
12
13 public MultiMap() {
14 noduplicates = false;
15 keyClass = Object.class;
16 valueClass = Object.class;
17 }
18
19 public MultiMap(Class<K> keyClass, Class<V> valueClass, boolean noduplicates ) {
20 this.noduplicates = noduplicates;
21 this.keyClass = keyClass;
22 this.valueClass = valueClass;
23 }
24
25 @SuppressWarnings("unchecked") public boolean add( K key, V value ) {
26 assert keyClass.isInstance(key);
27 assert valueClass.isInstance(value);
28
29 List<V> set = get(key);
30 if ( set == null) {
31 set=new ArrayList<V>();
32 if ( valueClass != Object.class) {
33 set = Collections.checkedList(set, (Class<V>)valueClass);
34 }
35 put(key,set);
36 }else {
37 if (noduplicates) {
38 if ( set.contains(value))
39 return false;
40 }
41 }
42 return set.add(value);
43 }
44
45 @SuppressWarnings("unchecked") public boolean addAll( K key, Collection<? extends V> value ) {
46 assert keyClass.isInstance(key);
47 List<V> set = get(key);
48 if ( set == null) {
49 set=new ArrayList<V>();
50 if ( valueClass != Object.class) {
51 set = Collections.checkedList(set, (Class<V>)valueClass);
52 }
53 put(key,set);
54 } else
55 if ( noduplicates) {
56 boolean r=false;
57 for ( V v : value) {
58 assert valueClass.isInstance(v);
59 if ( !set.contains(value))
60 r|=set.add(v);
61 }
62 return r;
63 }
64 return set.addAll(value);
65 }
66
67 public boolean remove( K key, V value ) {
68 assert keyClass.isInstance(key);
69 assert valueClass.isInstance(value);
70
71 List<V> set = get(key);
72 if ( set == null) {
73 return false;
74 }
75 boolean result = set.remove(value);
76 if ( set.isEmpty())
77 remove(key);
78 return result;
79 }
80
81 public boolean removeAll( K key, Collection<V> value ) {
82 assert keyClass.isInstance(key);
83 List<V> set = get(key);
84 if ( set == null) {
85 return false;
86 }
87 boolean result = set.removeAll(value);
88 if ( set.isEmpty())
89 remove(key);
90 return result;
91 }
92
93 public Iterator<V> iterate(K key) {
94 assert keyClass.isInstance(key);
95 List<V> set = get(key);
96 if ( set == null)
97 return EMPTY.iterator();
98 return set.iterator();
99 }
100
101 public Iterator<V> all() {
102 return new Iterator<V>() {
103 Iterator<List<V>> master = values().iterator();
104 Iterator<V> current = null;
105
106 public boolean hasNext() {
107 if ( current == null || !current.hasNext()) {
108 if ( master.hasNext()) {
109 current = master.next().iterator();
110 return current.hasNext();
111 }
112 return false;
113 }
114 return true;
115 }
116
117 public V next() {
118 return current.next();
119 }
120
121 public void remove() {
122 current.remove();
123 }
124
125 };
126 }
127
128 public Map<K,V> flatten() {
129 Map<K,V> map = new LinkedHashMap<K,V>();
130 for ( Map.Entry<K, List<V>> entry : entrySet()) {
131 List<V> v = entry.getValue();
132 if ( v == null || v.isEmpty())
133 continue;
134
135 map.put(entry.getKey(), v.get(0));
136 }
137 return map;
138 }
139
140 public MultiMap<V,K> transpose() {
141 MultiMap<V,K> inverted = new MultiMap<V,K>();
142 for ( Map.Entry<K, List<V>> entry : entrySet()) {
143 K key = entry.getKey();
144
145 List<V> value = entry.getValue();
146 if ( value == null)
147 continue;
148
149 for ( V v : value)
150 inverted.add(v, key);
151 }
152
153 return inverted;
154 }
155}