• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*******************************************************************************
2  * Copyright 2011 See AUTHORS file.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *   http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  ******************************************************************************/
16 
17 package com.badlogic.gdx.utils;
18 
19 import java.util.Iterator;
20 import java.util.NoSuchElementException;
21 
22 import com.badlogic.gdx.utils.ObjectMap.Entries;
23 
24 /** An {@link ObjectMap} that also stores keys in an {@link Array} using the insertion order. There is some additional overhead for
25  * put and remove. Iteration over the {@link #entries()}, {@link #keys()}, and {@link #values()} is ordered and faster than an
26  * unordered map. Keys can also be accessed and the order changed using {@link #orderedKeys()}.
27  * @author Nathan Sweet */
28 public class OrderedMap<K, V> extends ObjectMap<K, V> {
29 	final Array<K> keys;
30 
31 	private Entries entries1, entries2;
32 	private Values values1, values2;
33 	private Keys keys1, keys2;
34 
OrderedMap()35 	public OrderedMap () {
36 		keys = new Array();
37 	}
38 
OrderedMap(int initialCapacity)39 	public OrderedMap (int initialCapacity) {
40 		super(initialCapacity);
41 		keys = new Array(capacity);
42 	}
43 
OrderedMap(int initialCapacity, float loadFactor)44 	public OrderedMap (int initialCapacity, float loadFactor) {
45 		super(initialCapacity, loadFactor);
46 		keys = new Array(capacity);
47 	}
48 
OrderedMap(ObjectMap<? extends K, ? extends V> map)49 	public OrderedMap (ObjectMap<? extends K, ? extends V> map) {
50 		super(map);
51 		keys = new Array(capacity);
52 	}
53 
put(K key, V value)54 	public V put (K key, V value) {
55 		if (!containsKey(key)) keys.add(key);
56 		return super.put(key, value);
57 	}
58 
remove(K key)59 	public V remove (K key) {
60 		keys.removeValue(key, false);
61 		return super.remove(key);
62 	}
63 
clear(int maximumCapacity)64 	public void clear (int maximumCapacity) {
65 		keys.clear();
66 		super.clear(maximumCapacity);
67 	}
68 
clear()69 	public void clear () {
70 		keys.clear();
71 		super.clear();
72 	}
73 
orderedKeys()74 	public Array<K> orderedKeys () {
75 		return keys;
76 	}
77 
iterator()78 	public Entries<K, V> iterator () {
79 		return entries();
80 	}
81 
82 	/** Returns an iterator for the entries in the map. Remove is supported. Note that the same iterator instance is returned each
83 	 * time this method is called. Use the {@link OrderedMapEntries} constructor for nested or multithreaded iteration. */
entries()84 	public Entries<K, V> entries () {
85 		if (entries1 == null) {
86 			entries1 = new OrderedMapEntries(this);
87 			entries2 = new OrderedMapEntries(this);
88 		}
89 		if (!entries1.valid) {
90 			entries1.reset();
91 			entries1.valid = true;
92 			entries2.valid = false;
93 			return entries1;
94 		}
95 		entries2.reset();
96 		entries2.valid = true;
97 		entries1.valid = false;
98 		return entries2;
99 	}
100 
101 	/** Returns an iterator for the values in the map. Remove is supported. Note that the same iterator instance is returned each
102 	 * time this method is called. Use the {@link OrderedMapValues} constructor for nested or multithreaded iteration. */
values()103 	public Values<V> values () {
104 		if (values1 == null) {
105 			values1 = new OrderedMapValues(this);
106 			values2 = new OrderedMapValues(this);
107 		}
108 		if (!values1.valid) {
109 			values1.reset();
110 			values1.valid = true;
111 			values2.valid = false;
112 			return values1;
113 		}
114 		values2.reset();
115 		values2.valid = true;
116 		values1.valid = false;
117 		return values2;
118 	}
119 
120 	/** Returns an iterator for the keys in the map. Remove is supported. Note that the same iterator instance is returned each time
121 	 * this method is called. Use the {@link OrderedMapKeys} constructor for nested or multithreaded iteration. */
keys()122 	public Keys<K> keys () {
123 		if (keys1 == null) {
124 			keys1 = new OrderedMapKeys(this);
125 			keys2 = new OrderedMapKeys(this);
126 		}
127 		if (!keys1.valid) {
128 			keys1.reset();
129 			keys1.valid = true;
130 			keys2.valid = false;
131 			return keys1;
132 		}
133 		keys2.reset();
134 		keys2.valid = true;
135 		keys1.valid = false;
136 		return keys2;
137 	}
138 
toString()139 	public String toString () {
140 		if (size == 0) return "{}";
141 		StringBuilder buffer = new StringBuilder(32);
142 		buffer.append('{');
143 		Array<K> keys = this.keys;
144 		for (int i = 0, n = keys.size; i < n; i++) {
145 			K key = keys.get(i);
146 			if (i > 0) buffer.append(", ");
147 			buffer.append(key);
148 			buffer.append('=');
149 			buffer.append(get(key));
150 		}
151 		buffer.append('}');
152 		return buffer.toString();
153 	}
154 
155 	static public class OrderedMapEntries<K, V> extends Entries<K, V> {
156 		private Array<K> keys;
157 
OrderedMapEntries(OrderedMap<K, V> map)158 		public OrderedMapEntries (OrderedMap<K, V> map) {
159 			super(map);
160 			keys = map.keys;
161 		}
162 
reset()163 		public void reset () {
164 			nextIndex = 0;
165 			hasNext = map.size > 0;
166 		}
167 
next()168 		public Entry next () {
169 			if (!hasNext) throw new NoSuchElementException();
170 			if (!valid) throw new GdxRuntimeException("#iterator() cannot be used nested.");
171 			entry.key = keys.get(nextIndex);
172 			entry.value = map.get(entry.key);
173 			nextIndex++;
174 			hasNext = nextIndex < map.size;
175 			return entry;
176 		}
177 
178 		public void remove () {
179 			if (currentIndex < 0) throw new IllegalStateException("next must be called before remove.");
180 			map.remove(entry.key);
181 		}
182 	}
183 
184 	static public class OrderedMapKeys<K> extends Keys<K> {
185 		private Array<K> keys;
186 
187 		public OrderedMapKeys (OrderedMap<K, ?> map) {
188 			super(map);
189 			keys = map.keys;
190 		}
191 
192 		public void reset () {
193 			nextIndex = 0;
194 			hasNext = map.size > 0;
195 		}
196 
197 		public K next () {
198 			if (!hasNext) throw new NoSuchElementException();
199 			if (!valid) throw new GdxRuntimeException("#iterator() cannot be used nested.");
200 			K key = keys.get(nextIndex);
201 			nextIndex++;
202 			hasNext = nextIndex < map.size;
203 			return key;
204 		}
205 
206 		public void remove () {
207 			if (currentIndex < 0) throw new IllegalStateException("next must be called before remove.");
208 			map.remove(keys.get(nextIndex - 1));
209 		}
210 	}
211 
212 	static public class OrderedMapValues<V> extends Values<V> {
213 		private Array keys;
214 
215 		public OrderedMapValues (OrderedMap<?, V> map) {
216 			super(map);
217 			keys = map.keys;
218 		}
219 
220 		public void reset () {
221 			nextIndex = 0;
222 			hasNext = map.size > 0;
223 		}
224 
225 		public V next () {
226 			if (!hasNext) throw new NoSuchElementException();
227 			if (!valid) throw new GdxRuntimeException("#iterator() cannot be used nested.");
228 			V value = (V)map.get(keys.get(nextIndex));
229 			nextIndex++;
230 			hasNext = nextIndex < map.size;
231 			return value;
232 		}
233 
234 		public void remove () {
235 			if (currentIndex < 0) throw new IllegalStateException("next must be called before remove.");
236 			map.remove(keys.get(nextIndex - 1));
237 		}
238 	}
239 }
240