| Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||
| HashMap | 
  | 
  | 0.0;0 | 
| 1 |  /* | |
| 2 |   * Licensed under the MIT License | |
| 3 |   *  | |
| 4 |   * Copyright 2010 (c) Flávio Silva, http://flsilva.com | |
| 5 |   * | |
| 6 |   * Permission is hereby granted, free of charge, to any person | |
| 7 |   * obtaining a copy of this software and associated documentation | |
| 8 |   * files (the "Software"), to deal in the Software without | |
| 9 |   * restriction, including without limitation the rights to use, | |
| 10 |   * copy, modify, merge, publish, distribute, sublicense, and/or sell | |
| 11 |   * copies of the Software, and to permit persons to whom the | |
| 12 |   * Software is furnished to do so, subject to the following | |
| 13 |   * conditions: | |
| 14 |   * | |
| 15 |   * The above copyright notice and this permission notice shall be | |
| 16 |   * included in all copies or substantial portions of the Software. | |
| 17 |   * | |
| 18 |   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | |
| 19 |   * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES | |
| 20 |   * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |
| 21 |   * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT | |
| 22 |   * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, | |
| 23 |   * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | |
| 24 |   * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR | |
| 25 |   * OTHER DEALINGS IN THE SOFTWARE. | |
| 26 |   *  | |
| 27 |   * http://www.opensource.org/licenses/mit-license.php | |
| 28 |   */ | |
| 29 | ||
| 30 | 1 |  package org.as3collections.maps | 
| 31 |  { | |
| 32 |          import org.as3collections.AbstractHashMap; | |
| 33 |          import org.as3collections.IIterator; | |
| 34 |          import org.as3collections.IMap; | |
| 35 |          import org.as3collections.iterators.MapIterator; | |
| 36 |          import org.as3coreaddendum.system.IEquatable; | |
| 37 | ||
| 38 |          /** | |
| 39 |           * Hash table based implementation of the <code>IMap</code> interface. | |
| 40 |           * This implementation provides all of the optional map operations, and permits <code>null</code> values and the <code>null</code> key. | |
| 41 |           * <p>This class makes no guarantees as to the order of the map. | |
| 42 |           * In particular, it does not guarantee that the order will remain constant over time.</p> | |
| 43 |           * <p>It's possible to create typed maps. | |
| 44 |           * You just sends the <code>HashMap</code> object to the wrapper <code>TypedMap</code> or uses the <code>MapUtil.getTypedMap</code>.</p> | |
| 45 |           *  | |
| 46 |           * @example | |
| 47 |           *  | |
| 48 |           * <listing version="3.0"> | |
| 49 |           * import org.as3collections.IMap; | |
| 50 |           * import org.as3collections.IList; | |
| 51 |           * import org.as3collections.maps.HashMap; | |
| 52 |           * import org.as3collections.maps.MapEntry; | |
| 53 |           *  | |
| 54 |           * var map1:IMap = new HashMap(); | |
| 55 |           * var tf1:TextField = new TextField(); | |
| 56 |           * var tf2:TextField = new TextField(); | |
| 57 |           *  | |
| 58 |           * map1                            // {} | |
| 59 |           * map1.containsKey("a")           // false | |
| 60 |           * map1.containsKey(tf2)           // false | |
| 61 |           * map1.containsValue(2)           // false | |
| 62 |           * map1.containsValue(tf1)         // false | |
| 63 |           * map1.isEmpty()                  // true | |
| 64 |           * map1.size()                     // 0 | |
| 65 |           *  | |
| 66 |           * map1.put("a", 1)                // null | |
| 67 |           * map1                            // {a=1} | |
| 68 |           * map1.isEmpty()                  // false | |
| 69 |           * map1.size()                     // 1 | |
| 70 |           * map1.containsKey("a")           // true | |
| 71 |           * map1.containsKey(tf2)           // false | |
| 72 |           * map1.containsValue(2)           // false | |
| 73 |           * map1.containsValue(tf1)         // false | |
| 74 |           *  | |
| 75 |           * map1.put("b", 2)                // null | |
| 76 |           * map1                            // {b=2,a=1} | |
| 77 |           * map1.isEmpty()                  // false | |
| 78 |           * map1.size()                     // 2 | |
| 79 |           * map1.containsKey("a")           // true | |
| 80 |           * map1.containsKey("b")           // true | |
| 81 |           * map1.containsKey(tf2)           // false | |
| 82 |           * map1.containsValue(2)           // true | |
| 83 |           *  | |
| 84 |           * map1.put("c", 3)                // null | |
| 85 |           * map1                            // {b=2,a=1,c=3} | |
| 86 |           * map1.size()                     // 3 | |
| 87 |           *  | |
| 88 |           * map1.put("tf1", tf1)            // null | |
| 89 |           * map1                            // {b=2,a=1,c=3,tf1=[object TextField]} | |
| 90 |           * map1.size()                     // 4 | |
| 91 |           * map1.containsValue(tf1)         // true | |
| 92 |           *  | |
| 93 |           * map1.put(tf2, "tf2")            // null | |
| 94 |           * map1                            // {b=2,[object TextField]=tf2,a=1,c=3,tf1=[object TextField]} | |
| 95 |           * map1.size()                     // 5 | |
| 96 |           * map1.containsKey(tf2)           // true | |
| 97 |           *  | |
| 98 |           * map1.put("a", 1.1)              // 1 | |
| 99 |           * map1                            // {b=2,[object TextField]=tf2,a=1.1,c=3,tf1=[object TextField]} | |
| 100 |           * map1.size()                     // 5 | |
| 101 |           *  | |
| 102 |           * map1.put("tf1", String)         // [object TextField] | |
| 103 |           * map1                            // {b=2,[object TextField]=tf2,a=1.1,c=3,tf1=[class String]} | |
| 104 |           * map1.size()                     // 5 | |
| 105 |           *  | |
| 106 |           * map1.put(tf2, "tf2.1")          // tf2 | |
| 107 |           * map1                            // {b=2,[object TextField]=tf2.1,a=1.1,c=3,tf1=[class String]} | |
| 108 |           * map1.size()                     // 5 | |
| 109 |           *  | |
| 110 |           * map1.put(Number, 999)           // null | |
| 111 |           * map1                            // {b=2,[object TextField]=tf2.1,[class Number]=999,a=1.1,c=3,tf1=[class String]} | |
| 112 |           * map1.size(): 6 | |
| 113 |           *  | |
| 114 |           * map1.getValue("b")              // 2 | |
| 115 |           *  | |
| 116 |           * map1.getValue(tf2)              // tf2.1 | |
| 117 |           *  | |
| 118 |           * map1.putAllByObject({fa:"fb",ga:"gb",ha:"hb"}); | |
| 119 |           *  | |
| 120 |           * map1                            // {b=2,[object TextField]=tf2.1,fa=fb,[class Number]=999,c=3,ha=hb,a=1.1,tf1=[class String],ga=gb} | |
| 121 |           *  | |
| 122 |           * map1.size()                     // 9 | |
| 123 |           *  | |
| 124 |           * map1.getValue("fa")             // fb | |
| 125 |           *  | |
| 126 |           * map1.remove("ga")               // gb | |
| 127 |           * map1                            // {b=2,[object TextField]=tf2.1,fa=fb,[class Number]=999,c=3,ha=hb,a=1.1,tf1=[class String]} | |
| 128 |           * map1.size()                     // 8 | |
| 129 |           *  | |
| 130 |           * map1.remove("fa")               // fb | |
| 131 |           * map1                            // {b=2,[object TextField]=tf2.1,[class Number]=999,c=3,ha=hb,a=1.1,tf1=[class String]} | |
| 132 |           * map1.size()                     // 7 | |
| 133 |           *  | |
| 134 |           * map1.remove(tf2)                // tf2.1 | |
| 135 |           * map1                            // {b=2,[class Number]=999,c=3,ha=hb,a=1.1,tf1=[class String]} | |
| 136 |           * map1.size()                     // 6 | |
| 137 |           *  | |
| 138 |           * map1.getValue("fa")             // null | |
| 139 |           * map1.getValue(tf2)              // null | |
| 140 |           *  | |
| 141 |           * var map2:IMap = map1.clone(); | |
| 142 |           *  | |
| 143 |           * map2                            // {b=2,a=1.1,[class Number]=999,c=3,tf1=[class String],ha=hb} | |
| 144 |           * map2.size()                     // 6 | |
| 145 |           * map2.isEmpty()                  // false | |
| 146 |           *  | |
| 147 |           * map1.equals(map2)               // true | |
| 148 |           * map2.equals(map1)               // true | |
| 149 |           * map2.equals(map2)               // true | |
| 150 |           *  | |
| 151 |           * map2.remove("b")                // 2 | |
| 152 |           * map2                            // {a=1.1,[class Number]=999,c=3,tf1=[class String],ha=hb} | |
| 153 |           * map2.equals(map2)               // true | |
| 154 |           * map2.size()                     // 5 | |
| 155 |           *  | |
| 156 |           * map1.equals(map2)               // false | |
| 157 |           * map2.equals(map1)               // false | |
| 158 |           *  | |
| 159 |           * map2.getValues()                // [1.1,999,3,[class String],hb] | |
| 160 |           *  | |
| 161 |           * var keysMap2:IList = map2.getKeys(); | |
| 162 |           *  | |
| 163 |           * keysMap2                        // [a,[class Number],c,tf1,ha] | |
| 164 |           *  | |
| 165 |           * keysMap2.remove("c")            // true | |
| 166 |           * keysMap2                        // [a,[class Number],tf1,ha] | |
| 167 |           * map2                            // {a=1.1,[class Number]=999,c=3,tf1=[class String],ha=hb} | |
| 168 |           * map2.size()                     // 5 | |
| 169 |           *  | |
| 170 |           * map2.removeAll(keysMap2)        // true | |
| 171 |           * map2                            // {c=3} | |
| 172 |           * map2.size()                     // 1 | |
| 173 |           * map2.isEmpty()                  // false | |
| 174 |           *  | |
| 175 |           * map2.clear(); | |
| 176 |           *  | |
| 177 |           * map2                            // {} | |
| 178 |           * map2.size()                     // 0 | |
| 179 |           * map2.isEmpty()                  // true | |
| 180 |           *  | |
| 181 |           * var entry:IMapEntry = new MapEntry("c", 3); | |
| 182 |           *  | |
| 183 |           * entry                           // c=3 | |
| 184 |           * map2.putEntry(entry)            // null | |
| 185 |           * map2                            // {c=3} | |
| 186 |           * map2.size()                     // 1 | |
| 187 |           *  | |
| 188 |           * map1                            // {b=2,[class Number]=999,c=3,ha=hb,a=1.1,tf1=[class String]} | |
| 189 |           * map1.retainAll(map2)            // true | |
| 190 |           * map1                            // {c=3} | |
| 191 |           * map1.size()                     // 1 | |
| 192 |           * map1.isEmpty()                  // false | |
| 193 |           *  | |
| 194 |           * map1.put("d", 4)                // null | |
| 195 |           * map1.put("e", 5)                // null | |
| 196 |           * map1.put("f", 6)                // null | |
| 197 |           *  | |
| 198 |           * map1                            // {c=3,d=4,f=6,e=5} | |
| 199 |           * map1.size()                     // 4 | |
| 200 |           *  | |
| 201 |           * var it:IIterator = map1.iterator(); | |
| 202 |           *  | |
| 203 |           * var e:*; | |
| 204 |           *  | |
| 205 |           * while (it.hasNext()) | |
| 206 |           * { | |
| 207 |           *  | |
| 208 |           *     e = it.next(); | |
| 209 |           *     trace(it.pointer() + "=" + e)    // c=3 | |
| 210 |           *  | |
| 211 |           *     e = it.next(); | |
| 212 |           *     trace(it.pointer() + "=" + e)    // d=4 | |
| 213 |           *  | |
| 214 |           *     if (e == 4) | |
| 215 |           *     { | |
| 216 |           *         it.remove(); | |
| 217 |           *     } | |
| 218 |           *  | |
| 219 |           *     e = it.next(); | |
| 220 |           *     trace(it.pointer() + "=" + e)    // f=6 | |
| 221 |           *  | |
| 222 |           *     e = it.next(); | |
| 223 |           *     trace(it.pointer() + "=" + e)    // e=5 | |
| 224 |           * } | |
| 225 |           *  | |
| 226 |           * map1                            // {c=3,f=6,e=5} | |
| 227 |           * map1.size()                     // 3 | |
| 228 |           * </listing> | |
| 229 |           *  | |
| 230 |           * @see org.as3collections.utils.MapUtil#getTypedMap() MapUtil.getTypedMap() | |
| 231 |           * @author Flávio Silva | |
| 232 |           */ | |
| 233 | public class HashMap extends AbstractHashMap  | |
| 234 |          { | |
| 235 |                  /** | |
| 236 |                   * Constructor, creates a new <code>HashMap</code> object. | |
| 237 |                   *  | |
| 238 |                   * @param         source                 a map with wich fill this map. | |
| 239 |                   * @param         weakKeys         instructs the backed <code>Dictionary</code> object to use "weak" references on object keys. If the only reference to an object is in the specified <code>Dictionary</code> object, the key is eligible for garbage collection and is removed from the table when the object is collected. | |
| 240 |                   */ | |
| 241 | public function HashMap(source:IMap = null, weakKeys:Boolean = false)  | |
| 242 |                  { | |
| 243 | 1 |                          super(source, weakKeys); | 
| 244 | 1 | }  | 
| 245 | ||
| 246 |                  /** | |
| 247 |                   * Removes all of the mappings from this map. | |
| 248 |                   * The map will be empty after this call returns. | |
| 249 |                   */ | |
| 250 | override public function clear(): void  | |
| 251 |                  { | |
| 252 | 1 | _init();  | 
| 253 | 1 | }  | 
| 254 | ||
| 255 |                  /** | |
| 256 |                   * Creates and return a new <code>HashMap</code> object containing all mappings in this map. | |
| 257 |                   *  | |
| 258 |                   * @return         a new <code>HashMap</code> object containing all mappings in this map. | |
| 259 |                    */ | |
| 260 |                  override public function clone(): * | |
| 261 |                  { | |
| 262 | 1 | return new HashMap(this);  | 
| 263 | }  | |
| 264 | ||
| 265 |                  /** | |
| 266 |                   * Returns an iterator over a set of mappings. | |
| 267 |                   * <p>This implementation returns a <code>MapIterator</code> object.</p> | |
| 268 |                   *  | |
| 269 |                   * @return         an iterator over a set of values. | |
| 270 |                   * @see         org.as3collections.iterators.MapIterator MapIterator | |
| 271 |                    */ | |
| 272 |                  override public function iterator(): IIterator | |
| 273 |                  { | |
| 274 | 1 | return new MapIterator(this);  | 
| 275 | }  | |
| 276 | ||
| 277 |                  /** | |
| 278 |                   * Associates the specified value with the specified key in this map. | |
| 279 |                   * If the map previously contained a mapping for the key, the old value is replaced by the specified value. (A map <code>m</code> is said to contain a mapping for a key <code>k</code> if and only if <code>m.containsKey(k)</code> would return <code>true</code>.)  | |
| 280 |                   *  | |
| 281 |                   * @param          key         key with which the specified value is to be associated. | |
| 282 |                   * @param          value         value to be associated with the specified key. | |
| 283 |                   * @return         the previous value associated with key, or <code>null</code> if there was no mapping for key. (A <code>null</code> return can also indicate that the map previously associated <code>null</code> with key, because this implementation supports <code>null</code> values.) | |
| 284 |                   */ | |
| 285 |                  override public function put(key:*, value:*): * | |
| 286 |                  { | |
| 287 | var oldValue:*;  | |
| 288 | ||
| 289 | 1 |                          if (containsKey(key)) oldValue = remove(key); | 
| 290 | ||
| 291 | 1 | _size++;  | 
| 292 | 1 | map[key] = value;  | 
| 293 | ||
| 294 | 1 |                          var countValues:int = values[value]; | 
| 295 | 1 |                          if (countValues < 1) countValues = 0; | 
| 296 | 1 | values[value] = ++countValues;  | 
| 297 | ||
| 298 | 1 | keyAdded(key);  | 
| 299 | 1 | valueAdded(value);  | 
| 300 | ||
| 301 | 1 |                          return oldValue; | 
| 302 | }  | |
| 303 | ||
| 304 |                  /** | |
| 305 |                   * Removes the mapping for a key from this map if it is present. | |
| 306 |                   * <p>Returns the value to which this map previously associated the key, or <code>null</code> if the map contained no mapping for the key. | |
| 307 |                   * A return value of <code>null</code> does not <em>necessarily</em> indicate that the map contained no mapping for the key. | |
| 308 |                   * It's possible that the map explicitly mapped the key to <code>null</code>.</p> | |
| 309 |                   * <p>The map will not contain a mapping for the specified key once the call returns.</p> | |
| 310 |                   *  | |
| 311 |                   * @param          key         the key whose mapping is to be removed from the map. | |
| 312 |                   * @return         the previous value associated with key, or <code>null</code> if there was no mapping for <code>key</code>. | |
| 313 |                   */ | |
| 314 |                  override public function remove(key:*): * | |
| 315 |                  { | |
| 316 | 1 |                          if (allKeysEquatable && key is IEquatable) | 
| 317 |                          { | |
| 318 | 1 |                                  return removeByEquality(key); | 
| 319 | }  | |
| 320 |                          else | |
| 321 |                          { | |
| 322 | 1 |                                  return removeByInstance(key); | 
| 323 | }  | |
| 324 | }  | |
| 325 | ||
| 326 |                  /** | |
| 327 |                   * @private | |
| 328 |                   */ | |
| 329 | override protected function valueRemoved(value:*): void  | |
| 330 |                  { | |
| 331 | 1 |                          super.valueRemoved(value); | 
| 332 | ||
| 333 | 1 |                          var countValues:int = values[value]; | 
| 334 | ||
| 335 | 1 |                          if (countValues > 1) | 
| 336 |                          { | |
| 337 | 1 | values[value] = --countValues;  | 
| 338 | }  | |
| 339 |                          else | |
| 340 |                          { | |
| 341 | 1 | delete values[value];  | 
| 342 | }  | |
| 343 | 1 | }  | 
| 344 | ||
| 345 |                  /** | |
| 346 |                   * @private | |
| 347 |                   */ | |
| 348 |                  private function removeByEquality(key:*): * | |
| 349 |                  { | |
| 350 | 1 | var it:IIterator = iterator();  | 
| 351 | var $key:IEquatable;  | |
| 352 | var value:*;  | |
| 353 | var returnValue:*;  | |
| 354 | ||
| 355 | 1 |                          while (it.hasNext()) | 
| 356 |                          { | |
| 357 | 1 | value = it.next();  | 
| 358 | 1 | $key = it.pointer();  | 
| 359 | ||
| 360 | 1 |                                  if ($key.equals(key)) | 
| 361 |                                  { | |
| 362 | 1 | returnValue = value;  | 
| 363 | 1 | delete map[$key];  | 
| 364 | 1 | _size--;  | 
| 365 | ||
| 366 | 1 | keyRemoved(key);  | 
| 367 | 1 | valueRemoved(value);  | 
| 368 | ||
| 369 | 1 |                                          break; | 
| 370 | }  | |
| 371 | }  | |
| 372 | ||
| 373 | 1 |                          return returnValue; | 
| 374 | }  | |
| 375 | ||
| 376 |                  /** | |
| 377 |                   * @private | |
| 378 |                   */ | |
| 379 |                  private function removeByInstance(key:*): * | |
| 380 |                  { | |
| 381 | 1 | if (map[key] === undefined) return null;  | 
| 382 | ||
| 383 | 1 | var old:* = map[key];  | 
| 384 | 1 | delete map[key];  | 
| 385 | 1 | _size--;  | 
| 386 | ||
| 387 | 1 | keyRemoved(key);  | 
| 388 | 1 | valueRemoved(old);  | 
| 389 | ||
| 390 | 1 |                          return old; | 
| 391 | }  | |
| 392 | ||
| 393 | }  | |
| 394 | ||
| 395 | }  |