001package squidpony.squidmath; 002 003import squidpony.ArrayTools; 004 005import java.io.Serializable; 006import java.util.ArrayList; 007import java.util.Iterator; 008import java.util.SortedSet; 009 010/** 011 * An ordered multi-directional map-like data structure with two sets of keys and one list of values. 012 * 013 * This is structured so that the two keys, of types A and B, are always associated with each other and the same value, 014 * of type Q, but may have their index in the ordering change. You can look up a B key with an A key or index, an A key 015 * with a B key or index, and a Q value with an A key, B key, or index. 016 * Created by Tommy Ettinger on 10/27/2016. 017 */ 018public class K2V1<A, B, Q> implements Serializable { 019 private static final long serialVersionUID = 1L; 020 public K2<A, B> keys; 021 public ArrayList<Q> values; 022 023 /** 024 * Constructs an empty K2V1 with the default parameters: 32 expected indices and a load factor of 0.5f. 025 */ 026 public K2V1() 027 { 028 this(32, 0.5f); 029 } 030 031 /** 032 * Constructs an empty K2V1 that can hold {@code expected} indices before resizing and has a load factor of 0.5f. 033 * @param expected the expected number of items of any single type that this will hold; each put counts as one item 034 */ 035 public K2V1(int expected) 036 { 037 this(expected, 0.5f); 038 } 039 040 /** 041 * Constructs an empty K2V1 that can hold {@code expected} indices before resizing and has load factor {@code f}. 042 * @param expected the expected number of items of any single type that this will hold; each put counts as one item 043 * @param f the load factor; must be greater than 0 and less than 1, but should ideally be between 0.2 and 0.8 044 */ 045 public K2V1(int expected, float f) 046 { 047 keys = new K2<>(expected, f); 048 values = new ArrayList<>(expected); 049 } 050 051 /** 052 * Constructs a K2V1 from an A iterable, a B iterable, and a Q iterable, where the A and B items should be unique 053 * (if they aren't, each item that would be associated with a duplicate A or B will be skipped). The K2V1 this 054 * constructs will only take enough items from all Iterables to exhaust the shortest Iterable, so the lengths can be 055 * different between arguments. If there are no duplicate A keys or duplicate B keys (e.g. you can have an A key 056 * that is equal to a B key, but not to another A key), then all items will be used in the order the Iterable 057 * parameters provide them; otherwise the keys and value that would be associated with a duplicate are skipped. 058 * @param aKeys an Iterable of A keys; if null will be considered empty and this K2V1 will be empty 059 * @param bKeys an Iterable of B keys; if null will be considered empty and this K2V1 will be empty 060 * @param qValues an Iterable of Q values; if null will be considered empty and this K2V1 will be empty 061 */ 062 public K2V1(Iterable<A> aKeys, Iterable<B> bKeys, Iterable<Q> qValues) 063 { 064 this(32, 0.5f); 065 putAll(aKeys, bKeys, qValues); 066 } 067 068 /** 069 * Constructs a K2V1 from another K2V1 with ths same A, B, and Q types. This will have an expected size equal to the 070 * current size of other, use the same load factor (f) as other, and will have the same items put into it in the 071 * same order. 072 * @param other a K2V1 to copy into this; must have the same A, B, and Q types 073 */ 074 public K2V1(K2V1<A, B, Q> other) 075 { 076 this(other == null ? 32 : other.size(), other == null ? 0.5f : other.keys.keysA.f); 077 putAll(other); 078 } 079 /** 080 * Returns true if this contains the A, key, in its collection of A items, or false otherwise. 081 * @param key the A to check the presence of 082 * @return true if key is present in this; false otherwise 083 */ 084 public boolean containsA(A key) { return keys.containsA(key); } 085 /** 086 * Returns true if this contains the B, key, in its collection of B items, or false otherwise. 087 * @param key the B to check the presence of 088 * @return true if key is present in this; false otherwise 089 */ 090 public boolean containsB(B key) { return keys.containsB(key); } 091 092 /** 093 * Returns true if this contains at least one Q value, or false otherwise 094 * @param value the value to check 095 * @return true if value is present at least once in this collection's Q collection 096 */ 097 public boolean containsQ(Q value) { return values.contains(value); } 098 099 /** 100 * Returns true if index is between 0 (inclusive) and {@link #size()} (exclusive), or false otherwise. 101 * @param index the index to check 102 * @return true if index is a valid index in the ordering of this K2V1 103 */ 104 public boolean containsIndex(int index) { return keys.containsIndex(index); } 105 106 /** 107 * Given an A object key, finds the position in the ordering which that A has, or -1 if key is not present. 108 * Unlike {@link java.util.List#indexOf(Object)}, this runs in constant time. 109 * @param key the A to find the position of 110 * @return the int index of key in the ordering, or -1 if it is not present 111 */ 112 public int indexOfA(Object key) 113 { 114 return keys.indexOfA(key); 115 } 116 /** 117 * Given a B object key, finds the position in the ordering which that B has, or -1 if key is not present. 118 * Unlike {@link java.util.List#indexOf(Object)}, this runs in constant time. 119 * @param key the B to find the position of 120 * @return the int index of key in the ordering, or -1 if it is not present 121 */ 122 public int indexOfB(Object key) 123 { 124 return keys.indexOfB(key); 125 } 126 127 /** 128 * Given a Q value, finds the first position in the ordering which contains that value, or -1 if not present. 129 * Runs in linear time normally, since this uses {@link ArrayList#indexOf(Object)} to search the values. 130 * @param value the value to find the position of, which should be a Q 131 * @return the first int index of value in the ordering, or -1 if it is not present 132 */ 133 public int indexOfQ(Object value) { 134 //noinspection SuspiciousMethodCalls 135 return values.indexOf(value); 136 } 137 138 /** 139 * Given an int index, finds the associated A key (using index as a point in the ordering). 140 * @param index an int index into this K2V1 141 * @return the A object with index for its position in the ordering, or null if index was invalid 142 */ 143 public A getAAt(int index) 144 { 145 return keys.getAAt(index); 146 } 147 /** 148 * Given an int index, finds the associated B key (using index as a point in the ordering). 149 * @param index an int index into this K2V1 150 * @return the B object with index for its position in the ordering, or null if index was invalid 151 */ 152 public B getBAt(int index) 153 { 154 return keys.getBAt(index); 155 } 156 157 /** 158 * Given an int index, finds the associated Q value (using index as a point in the ordering). 159 * @param index an int index into this K2V1 160 * @return the Q value with index for its position in the ordering, or null if index was invalid 161 */ 162 public Q getQAt(int index) 163 { 164 if(index < 0 || index >= keys.keysA.size) 165 return null; 166 return values.get(index); 167 } 168 169 /** 170 * Given an A object, finds the associated B object (it will be at the same point in the ordering). 171 * @param key an A object to use as a key 172 * @return the B object associated with key, or null if key was not present 173 */ 174 public B getBFromA(Object key) 175 { 176 return keys.getBFromA(key); 177 } 178 179 /** 180 * Given a B object, finds the associated A object (it will be at the same point in the ordering). 181 * @param key a B object to use as a key 182 * @return the A object associated with key, or null if key was not present 183 */ 184 public A getAFromB(Object key) 185 { 186 return keys.getAFromB(key); 187 } 188 /** 189 * Given an A object, finds the associated Q value (it will be at the same point in the ordering). 190 * @param key an A object to use as a key 191 * @return the Q value associated with key, or null if key was not present 192 */ 193 public Q getQFromA(Object key) 194 { 195 int idx = keys.indexOfA(key); 196 if(idx >= 0) 197 return values.get(idx); 198 return null; 199 } 200 /** 201 * Given a B object, finds the associated Q value (it will be at the same point in the ordering). 202 * @param key a B object to use as a key 203 * @return the Q value associated with key, or null if key was not present 204 */ 205 public Q getQFromB(Object key) 206 { 207 int idx = keys.indexOfB(key); 208 if(idx >= 0) 209 return values.get(idx); 210 return null; 211 } 212 213 /** 214 * Gets a random A from this K2V1 using the given IRNG. 215 * @param random generates a random index to get an A with 216 * @return a randomly chosen A, or null if this is empty 217 */ 218 public A randomA(IRNG random) 219 { 220 return keys.randomA(random); 221 } 222 223 /** 224 * Gets a random B from this K2V1 using the given IRNG. 225 * @param random generates a random index to get a B with 226 * @return a randomly chosen B, or null if this is empty 227 */ 228 public B randomB(IRNG random) 229 { 230 return keys.randomB(random); 231 } 232 233 /** 234 * Gets a random Q from this K2V1 using the given IRNG. 235 * @param random generates a random index to get a Q with 236 * @return a randomly chosen Q, or null if this is empty 237 */ 238 public Q randomQ(IRNG random) { 239 if(random == null || values.isEmpty()) 240 return null; 241 return values.get(random.nextInt(values.size())); 242 } 243 244 /** 245 * Changes an existing A key, {@code past}, to another A key, {@code future}, if past exists in this K2V1 246 * and future does not yet exist in this K2V1. This will retain past's point in the ordering for future, so 247 * the associated other key(s) will still be associated in the same way. 248 * @param past an A key, that must exist in this K2V1's A keys, and will be changed 249 * @param future an A key, that cannot currently exist in this K2V1's A keys, but will if this succeeds 250 * @return this for chaining 251 */ 252 public K2V1<A, B, Q> alterA(A past, A future) 253 { 254 keys.alterA(past, future); 255 return this; 256 } 257 258 /** 259 * Changes an existing B key, {@code past}, to another B key, {@code future}, if past exists in this K2V1 260 * and future does not yet exist in this K2V1. This will retain past's point in the ordering for future, so 261 * the associated other key(s) will still be associated in the same way. 262 * @param past a B key, that must exist in this K2V1's B keys, and will be changed 263 * @param future a B key, that cannot currently exist in this K2V1's B keys, but will if this succeeds 264 * @return this for chaining 265 */ 266 public K2V1<A, B, Q> alterB(B past, B future) 267 { 268 keys.alterB(past, future); 269 return this; 270 } 271 272 /** 273 * Changes the A key at {@code index} to another A key, {@code future}, if index is valid and future does not 274 * yet exist in this K2V1. The position in the ordering for future will be the same as index, and the same 275 * as the key this replaced, if this succeeds, so the other key(s) at that position will still be associated in 276 * the same way. 277 * @param index a position in the ordering to change; must be at least 0 and less than {@link #size()} 278 * @param future an A key, that cannot currently exist in this K2V1's A keys, but will if this succeeds 279 * @return this for chaining 280 */ 281 public K2V1<A, B, Q> alterAAt(int index, A future) 282 { 283 keys.alterAAt(index, future); 284 return this; 285 } 286 287 288 /** 289 * Changes the B key at {@code index} to another B key, {@code future}, if index is valid and future does not 290 * yet exist in this K2V1. The position in the ordering for future will be the same as index, and the same 291 * as the key this replaced, if this succeeds, so the other key(s) at that position will still be associated in 292 * the same way. 293 * @param index a position in the ordering to change; must be at least 0 and less than {@link #size()} 294 * @param future a B key, that cannot currently exist in this K2V1's B keys, but will if this succeeds 295 * @return this for chaining 296 */ 297 public K2V1<A, B, Q> alterBAt(int index, B future) 298 { 299 keys.alterBAt(index, future); 300 return this; 301 } 302 303 /** 304 * Changes the Q value at {@code index} to another Q value, {@code future}, if index is valid. The position in the 305 * ordering for future will be the same as index, and the same as the key this replaced, if this succeeds, so the 306 * keys at that position will still be associated in the same way. 307 * @param index a position in the ordering to change; must be at least 0 and less than {@link #size()} 308 * @param future a Q value that will be set at the given index if this succeeds 309 * @return this for chaining 310 */ 311 public K2V1<A, B, Q> alterQAt(int index, Q future) 312 { 313 values.set(index, future); 314 return this; 315 } 316 /** 317 * Adds an A key, a B key, and a Q value at the same point in the ordering (the end) to this K2V1. Neither aKey nor 318 * bKey can be present in this collection before this is called. If you want to change or update an existing key, 319 * use {@link #alterA(Object, Object)} or {@link #alterB(Object, Object)}. The value, qValue, has no restrictions. 320 * @param aKey an A key to add; cannot already be present 321 * @param bKey a B key to add; cannot already be present 322 * @param qValue a Q value to add; can be present any number of times 323 * @return true if this collection changed as a result of this call 324 */ 325 326 public boolean put(A aKey, B bKey, Q qValue) 327 { 328 if(keys.put(aKey, bKey)) 329 { 330 values.add(qValue); 331 return true; 332 } 333 return false; 334 } 335 336 /** 337 * Adds an A key, a B key, and a Q value at the given index in the ordering to this K2V1. Neither a nor b can be 338 * present in this collection before this is called. If you want to change or update an existing key, use 339 * {@link #alterA(Object, Object)} or {@link #alterB(Object, Object)}. The value, q, has no restrictions. The index 340 * this is given should be at least 0 and no greater than {@link #size()}. 341 * @param index the point in the ordering to place a and b into; later entries will be shifted forward 342 * @param a an A key to add; cannot already be present 343 * @param b a B key to add; cannot already be present 344 * @param q a Q value to add; can be present any number of times 345 * @return true if this collection changed as a result of this call 346 */ 347 public boolean putAt(int index, A a, B b, Q q) 348 { 349 if(keys.putAt(index, a, b)) 350 { 351 values.add(index, q); 352 return true; 353 } 354 return false; 355 } 356 public boolean putAll(Iterable<A> aKeys, Iterable<B> bKeys, Iterable<Q> qValues) 357 { 358 if(aKeys == null || bKeys == null || qValues == null) return false; 359 Iterator<A> aIt = aKeys.iterator(); 360 Iterator<B> bIt = bKeys.iterator(); 361 Iterator<Q> qIt = qValues.iterator(); 362 boolean changed = false; 363 while (aIt.hasNext() && bIt.hasNext() && qIt.hasNext()) 364 { 365 changed = put(aIt.next(), bIt.next(), qIt.next()) || changed; 366 } 367 return changed; 368 } 369 /** 370 * Puts all unique A and B keys in {@code other} into this K2V1, respecting other's ordering. If an A or a B in other 371 * is already present when this would add one, this will not put the A and B keys at that point in the iteration 372 * order, and will place the next unique A and B it finds in the arguments at that position instead. 373 * @param other another K2V1 collection with the same A and B types 374 * @return true if this collection changed as a result of this call 375 */ 376 public boolean putAll(K2V1<A, B, Q> other) 377 { 378 if(other == null) return false; 379 boolean changed = false; 380 int sz = other.size(); 381 for (int i = 0; i < sz; i++) { 382 changed = put(other.getAAt(i), other.getBAt(i), other.getQAt(i)) || changed; 383 } 384 return changed; 385 } 386 /** 387 * Removes a given A key, if {@code removing} exists in this K2V1's A keys, and also removes any B key or Q value 388 * associated with its point in the ordering. 389 * @param removing the A key to remove 390 * @return this for chaining 391 */ 392 public K2V1<A, B, Q> removeA(A removing) 393 { 394 int i = keys.keysA.removeInt(removing); 395 if(i >= 0) { 396 keys.keysB.removeAt(i); 397 values.remove(i); 398 } 399 return this; 400 } 401 402 /** 403 * Removes a given B key, if {@code removing} exists in this K2V1's B keys, and also removes any A key or Q value 404 * associated with its point in the ordering. 405 * @param removing the B key to remove 406 * @return this for chaining 407 */ 408 public K2V1<A, B, Q> removeB(B removing) 409 { 410 int i = keys.keysB.removeInt(removing); 411 if(i >= 0) { 412 keys.keysA.removeAt(i); 413 values.remove(i); 414 } 415 return this; 416 } 417 /** 418 * Removes a given point in the ordering, if {@code index} is at least 0 and less than {@link #size()}. 419 * @param index the position in the ordering to remove 420 * @return this for chaining 421 */ 422 public K2V1<A, B, Q> removeAt(int index) 423 { 424 if (index >= 0 && index < keys.keysA.size) { 425 keys.removeAt(index); 426 values.remove(index); 427 } 428 return this; 429 } 430 431 /** 432 * Reorders this K2V1 using {@code ordering}, which have the same length as this K2V1's {@link #size()} 433 * and can be generated with {@link ArrayTools#range(int)} (which, if applied, would produce no 434 * change to the current ordering), {@link IRNG#randomOrdering(int)} (which gives a random ordering, and if 435 * applied immediately would be the same as calling {@link #shuffle(IRNG)}), or made in some other way. If you 436 * already have an ordering and want to make a different ordering that can undo the change, you can use 437 * {@link ArrayTools#invertOrdering(int[])} called on the original ordering. 438 * @param ordering an int array or vararg that should contain each int from 0 to {@link #size()} (or less) 439 * @return this for chaining 440 */ 441 public K2V1<A, B, Q> reorder(int... ordering) 442 { 443 keys.reorder(ordering); 444 ArrayTools.reorder(values, ordering); 445 return this; 446 } 447 448 /** 449 * Generates a random ordering with rng and applies the same ordering to all keys and values this has; they will 450 * maintain their current association to other keys and values but their ordering/indices will change. 451 * @param rng an IRNG to produce the random ordering this will use 452 * @return this for chaining 453 */ 454 public K2V1<A, B, Q> shuffle(IRNG rng) 455 { 456 int[] ordering = rng.randomOrdering(keys.keysA.size); 457 keys.reorder(ordering); 458 ArrayTools.reorder(values, ordering); 459 return this; 460 } 461 /** 462 * Creates a new iterator over the A keys this holds. This can be problematic for garbage collection if called very 463 * frequently; it may be better to access items by index (which also lets you access other keys associated with that 464 * index) using {@link #getAAt(int)} in a for(int i=0...) loop. 465 * @return a newly-created iterator over this K2V1's A keys 466 */ 467 public Iterator<A> iteratorA() 468 { 469 return keys.iteratorA(); 470 } 471 472 /** 473 * Creates a new iterator over the B keys this holds. This can be problematic for garbage collection if called very 474 * frequently; it may be better to access items by index (which also lets you access other keys associated with that 475 * index) using {@link #getBAt(int)} in a for(int i=0...) loop. 476 * @return a newly-created iterator over this K2V1's B keys 477 */ 478 public Iterator<B> iteratorB() 479 { 480 return keys.iteratorB(); 481 } 482 483 /** 484 * Creates a new iterator over the Q values this holds. This can be problematic for garbage collection if called 485 * very frequently; it may be better to access values by index (which also lets you access other keys associated 486 * with that index) using {@link #getQAt(int)} in a for(int i=0...) loop. 487 * @return a newly-created iterator over this K2V1's Q values 488 */ 489 public Iterator<Q> iteratorQ() 490 { 491 return values.iterator(); 492 } 493 494 /** 495 * Gets and caches the A keys as a Collection that implements SortedSet (and so also implements Set). It retains the 496 * current ordering. This Set is shared with this collection; it is not a copy, and changes to the returned Set will 497 * affect this data structure. 498 * @return the A keys as a SortedSet 499 */ 500 public SortedSet<A> getSetA() 501 { 502 return keys.getSetA(); 503 } 504 505 /** 506 * Gets and caches the B keys as a Collection that implements SortedSet (and so also implements Set). It retains the 507 * current ordering. This Set is shared with this collection; it is not a copy, and changes to the returned Set will 508 * affect this data structure. 509 * @return the B keys as a SortedSet 510 */ 511 public SortedSet<B> getSetB() 512 { 513 return keys.getSetB(); 514 } 515 516 /** 517 * Returns a separate (shallow) copy of the set of A keys as an {@link OrderedSet}. 518 * To be called sparingly, since this allocates a new OrderedSet instead of reusing one. This can be useful if you 519 * were going to copy the set produced by {@link #getSetA()} anyway. 520 * @return the A keys as an OrderedSet 521 */ 522 public OrderedSet<A> getOrderedSetA() { 523 return keys.getOrderedSetA(); 524 } 525 526 /** 527 * Returns a separate (shallow) copy of the set of B keys as an {@link OrderedSet}. 528 * To be called sparingly, since this allocates a new OrderedSet instead of reusing one. This can be useful if you 529 * were going to copy the set produced by {@link #getSetB()} anyway. 530 * @return the B keys as an OrderedSet 531 */ 532 public OrderedSet<B> getOrderedSetB() { 533 return keys.getOrderedSetB(); 534 } 535 536 /** 537 * Gets the Q values as a shared reference to the ArrayList of Q this uses; like {@link #getSetA()} and 538 * {@link #getSetB()}, changes made to the returned list will also change this data structure. 539 * It retains the current ordering. 540 * @return the Q values as an ArrayList, shared with this K2V1 541 */ 542 public ArrayList<Q> getListQ() 543 { 544 return values; 545 } 546 547 /** 548 * Gets the Q values as a freshly-copied ArrayList of Q; unlike {@link #getSetA()} or {@link #getSetB()}, this does 549 * not cache the value list. It retains the current ordering. 550 * @return the Q values as an ArrayList, copied from this K2V1's internal list 551 */ 552 public ArrayList<Q> getArrayListQ() 553 { 554 return new ArrayList<>(values); 555 } 556 557 /** 558 * Gets the size of this collection. That's the number of A keys, which is always the same as the number of B keys, 559 * which is always the same as the number of indices, which is also always the same as the size of the values List. 560 * @return the current number of indices in this K2V1, which can be thought of as the number of A keys 561 */ 562 public int size() 563 { 564 return keys.keysA.size; 565 } 566 567 /** 568 * I think you can guess what this does. 569 * @return true if there are no items in this K2V1; false if there are items present 570 */ 571 public boolean isEmpty() 572 { 573 return values.isEmpty(); 574 } 575 576 public int keyCount() { 577 return 2; 578 } 579 public int valueCount() { 580 return 1; 581 } 582 583}