1 /* 2 * Hunt - A refined core library for D programming language. 3 * 4 * Copyright (C) 2018-2019 HuntLabs 5 * 6 * Website: https://www.huntlabs.net/ 7 * 8 * Licensed under the Apache-2.0 License. 9 * 10 */ 11 12 module hunt.collection.ArrayList; 13 14 import std.algorithm; 15 import std.array; 16 // import std.container.array; 17 import std.conv; 18 import std.range; 19 import std.traits; 20 21 import hunt.Exceptions; 22 import hunt.collection.Array; 23 import hunt.collection.AbstractList; 24 import hunt.collection.Collection; 25 import hunt.collection.List; 26 import hunt.util.Comparator; 27 import hunt.util.Functional; 28 29 /** 30 */ 31 class ArrayList(E) : AbstractList!E { 32 33 /** 34 * Default initial capacity. 35 */ 36 private enum int DEFAULT_CAPACITY = 10; 37 38 /** 39 * Shared empty array instance used for empty instances. 40 */ 41 private enum Object[] EMPTY_ELEMENTDATA = []; 42 43 /** 44 * Shared empty array instance used for default sized empty instances. We 45 * distinguish this from EMPTY_ELEMENTDATA to know how much to inflate when 46 * first element is added. 47 */ 48 private enum Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = []; 49 50 51 protected Array!(E) _array; 52 53 /** 54 * Constructs an empty list with the specified initial capacity. 55 * 56 * @param initialCapacity the initial capacity of the list 57 * @throws Exception if the specified initial capacity 58 * is negative 59 */ 60 this(int initialCapacity) { 61 _array.reserve(initialCapacity); 62 super(); 63 // if (initialCapacity > 0) { 64 // this._array = new Object[initialCapacity]; 65 // } else if (initialCapacity == 0) { 66 // this._array = EMPTY_ELEMENTDATA; 67 // } else { 68 // throw new Exception("Illegal Capacity: "+ 69 // initialCapacity); 70 // } 71 } 72 73 /** 74 * Constructs an empty list with an initial capacity of ten. 75 */ 76 this() { 77 // this._array = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; 78 _array.reserve(16); 79 super(); 80 } 81 82 /** 83 * Constructs a list containing the elements of the specified 84 * collection, in the order they are returned by the collection's 85 * iterator. 86 * 87 * @param c the collection whose elements are to be placed into this list 88 * @throws NullPointerException if the specified collection is null 89 */ 90 this(ref Array!E arr) { 91 _array = arr; 92 } 93 94 this(E[] arr) { 95 _array.insertBack(arr); 96 } 97 98 this(Collection!E c) { 99 foreach(E e; c) { 100 _array.insertBack(e); 101 } 102 } 103 104 /** 105 * Trims the capacity of this <tt>ArrayList</tt> instance to be the 106 * list's current size. An application can use this operation to minimize 107 * the storage of an <tt>ArrayList</tt> instance. 108 */ 109 // void trimToSize() { 110 // modCount++; 111 // if (size < _array.length) { 112 // _array = (size == 0) 113 // ? EMPTY_ELEMENTDATA 114 // : Arrays.copyOf(_array, size); 115 // } 116 // } 117 118 /** 119 * Increases the capacity of this <tt>ArrayList</tt> instance, if 120 * necessary, to ensure that it can hold at least the number of elements 121 * specified by the minimum capacity argument. 122 * 123 * @param minCapacity the desired minimum capacity 124 */ 125 // void ensureCapacity(int minCapacity) { 126 // int minExpand = (_array != DEFAULTCAPACITY_EMPTY_ELEMENTDATA) 127 // // any size if not default element table 128 // ? 0 129 // // larger than default for default empty table. It's already 130 // // supposed to be at default size. 131 // : DEFAULT_CAPACITY; 132 133 // if (minCapacity > minExpand) { 134 // ensureExplicitCapacity(minCapacity); 135 // } 136 // } 137 138 // private void ensureCapacityInternal(int minCapacity) { 139 // if (_array == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { 140 // minCapacity = std.algorithm.max(DEFAULT_CAPACITY, minCapacity); 141 // } 142 143 // ensureExplicitCapacity(minCapacity); 144 // } 145 146 // private void ensureExplicitCapacity(int minCapacity) { 147 // modCount++; 148 149 // // overflow-conscious code 150 // if (minCapacity - _array.length > 0) 151 // grow(minCapacity); 152 // } 153 154 /** 155 * The maximum size of array to allocate. 156 * Some VMs reserve some header words in an array. 157 * Attempts to allocate larger arrays may result in 158 * OutOfMemoryError: Requested array size exceeds VM limit 159 */ 160 private enum int MAX_ARRAY_SIZE = int.max - 8; 161 162 163 164 /** 165 * Returns the number of elements in this list. 166 * 167 * @return the number of elements in this list 168 */ 169 override int size() { 170 return cast(int)_array.length; 171 } 172 173 /** 174 * Returns <tt>true</tt> if this list contains no elements. 175 * 176 * @return <tt>true</tt> if this list contains no elements 177 */ 178 override bool isEmpty() { 179 return _array.empty; 180 } 181 182 /** 183 * Returns <tt>true</tt> if this list contains the specified element. 184 * More formally, returns <tt>true</tt> if and only if this list contains 185 * at least one element <tt>e</tt> such that 186 * <tt>(o==null ? e==null : o.equals(e))</tt>. 187 * 188 * @param o element whose presence in this list is to be tested 189 * @return <tt>true</tt> if this list contains the specified element 190 */ 191 override bool contains(E o) const{ 192 return _array[].canFind(o); 193 } 194 195 196 /** 197 * Returns a shallow copy of this <tt>ArrayList</tt> instance. (The 198 * elements themselves are not copied.) 199 * 200 * @return a clone of this <tt>ArrayList</tt> instance 201 */ 202 // Object clone() { 203 204 // } 205 206 /** 207 * Returns an array containing all of the elements in this list 208 * in proper sequence (from first to last element). 209 * 210 * <p>The returned array will be "safe" in that no references to it are 211 * maintained by this list. (In other words, this method must allocate 212 * a new array). The caller is thus free to modify the returned array. 213 * 214 * <p>This method acts as bridge between array-based and collection-based 215 * APIs. 216 * 217 * @return an array containing all of the elements in this list in 218 * proper sequence 219 */ 220 override E[] toArray() { 221 return _array.array; 222 } 223 224 225 226 // Positional Access Operations 227 228 // 229 E elementData(int index) { 230 return _array[index]; 231 } 232 233 /** 234 * Returns the element at the specified position in this list. 235 * 236 * @param index index of the element to return 237 * @return the element at the specified position in this list 238 * @throws IndexOutOfBoundsException {@inheritDoc} 239 */ 240 override E get(int index) { 241 rangeCheck(index); 242 243 return cast(E)_array[index]; 244 } 245 246 /** 247 * Replaces the element at the specified position in this list with 248 * the specified element. 249 * 250 * @param index index of the element to replace 251 * @param element element to be stored at the specified position 252 * @return the element previously at the specified position 253 * @throws IndexOutOfBoundsException {@inheritDoc} 254 */ 255 override E set(int index, E element) { 256 rangeCheck(index); 257 258 E oldValue = _array[index]; 259 _array[index] = element; 260 return oldValue; 261 } 262 263 /** 264 * Appends the specified element to the end of this list. 265 * 266 * @param e element to be appended to this list 267 * @return <tt>true</tt> (as specified by {@link Collection#add}) 268 */ 269 // bool add(E e) { 270 // ensureCapacityInternal(size + 1); // Increments modCount!! 271 // _array[size++] = e; 272 // return true; 273 // } 274 override bool add(E e) { 275 return _array.insertBack(e) >=0; 276 } 277 278 /** 279 * Inserts the specified element at the specified position in this 280 * list. Shifts the element currently at that position (if any) and 281 * any subsequent elements to the right (adds one to their indices). 282 * 283 * @param index index at which the specified element is to be inserted 284 * @param element element to be inserted 285 * @throws IndexOutOfBoundsException {@inheritDoc} 286 */ 287 override void add(int index, E element) { 288 if(_array.length == 0) 289 _array.insertBack(element) ; 290 else { 291 if(index < 0) 292 index = 0; 293 _array.insertBefore(_array[index .. $], element); 294 } 295 } 296 297 alias add = AbstractList!(E).add; 298 299 /** 300 * Removes the element at the specified position in this list. 301 * Shifts any subsequent elements to the left (subtracts one from their 302 * indices). 303 * 304 * @param index the index of the element to be removed 305 * @return the element that was removed from the list 306 * @throws IndexOutOfBoundsException {@inheritDoc} 307 */ 308 override E removeAt(int index) { 309 E oldValue = _array[index]; 310 _array.linearRemove(_array[index .. index+1]); 311 return oldValue; 312 } 313 314 /** 315 * Removes the first occurrence of the specified element from this list, 316 * if it is present. If the list does not contain the element, it is 317 * unchanged. More formally, removes the element with the lowest index 318 * <tt>i</tt> such that 319 * <tt>(o==null ? get(i)==null : o.equals(get(i)))</tt> 320 * (if such an element exists). Returns <tt>true</tt> if this list 321 * contained the specified element (or equivalently, if this list 322 * changed as a result of the call). 323 * 324 * @param o element to be removed from this list, if present 325 * @return <tt>true</tt> if this list contained the specified element 326 */ 327 override bool remove(E o) 328 { 329 int index = indexOf(o); 330 if(index < 0) return false; 331 _array.linearRemove(_array[index .. index+1]); 332 return true; 333 } 334 335 override int indexOf(E o) { 336 for(size_t i=0; i<_array.length; i++) 337 { 338 static if(is(E == class)) 339 { 340 if(_array[i] is o) return cast(int)i; 341 } 342 else 343 { 344 if(_array[i] == o) return cast(int)i; 345 } 346 } 347 348 return -1; 349 } 350 351 override int lastIndexOf(E o) { 352 for(size_t i=_array.length -1; i>=0; i--) { 353 if(_array[i] == o) return cast(int)i; 354 } 355 356 return -1; 357 } 358 359 360 override int opApply(scope int delegate(ref E) dg) { 361 if(dg is null) 362 throw new NullPointerException(); 363 364 int result = 0; 365 foreach(E v; _array) { 366 result = dg(v); 367 if(result != 0) return result; 368 } 369 return result; 370 } 371 372 373 /* 374 * Private remove method that skips bounds checking and does not 375 * return the value removed. 376 */ 377 // private void fastRemove(int index) { 378 // modCount++; 379 // int numMoved = size - index - 1; 380 // if (numMoved > 0) 381 // System.arraycopy(_array, index+1, _array, index, 382 // numMoved); 383 // _array[--size] = null; // clear to let GC do its work 384 // } 385 386 /** 387 * Removes all of the elements from this list. The list will 388 * be empty after this call returns. 389 */ 390 override void clear() { 391 _array.clear(); 392 } 393 394 /** 395 * Appends all of the elements in the specified collection to the end of 396 * this list, in the order that they are returned by the 397 * specified collection's Iterator. The behavior of this operation is 398 * undefined if the specified collection is modified while the operation 399 * is in progress. (This implies that the behavior of this call is 400 * undefined if the specified collection is this list, and this 401 * list is nonempty.) 402 * 403 * @param c collection containing elements to be added to this list 404 * @return <tt>true</tt> if this list changed as a result of the call 405 * @throws NullPointerException if the specified collection is null 406 */ 407 // bool addAll(Collection<E> c) { 408 // Object[] a = c.toArray(); 409 // int numNew = a.length; 410 // ensureCapacityInternal(size + numNew); // Increments modCount 411 // System.arraycopy(a, 0, _array, size, numNew); 412 // size += numNew; 413 // return numNew != 0; 414 // } 415 416 /** 417 * Inserts all of the elements in the specified collection into this 418 * list, starting at the specified position. Shifts the element 419 * currently at that position (if any) and any subsequent elements to 420 * the right (increases their indices). The new elements will appear 421 * in the list in the order that they are returned by the 422 * specified collection's iterator. 423 * 424 * @param index index at which to insert the first element from the 425 * specified collection 426 * @param c collection containing elements to be added to this list 427 * @return <tt>true</tt> if this list changed as a result of the call 428 * @throws IndexOutOfBoundsException {@inheritDoc} 429 * @throws NullPointerException if the specified collection is null 430 */ 431 // bool addAll(int index, Collection<E> c) { 432 // rangeCheckForAdd(index); 433 434 // Object[] a = c.toArray(); 435 // int numNew = a.length; 436 // ensureCapacityInternal(size + numNew); // Increments modCount 437 438 // int numMoved = size - index; 439 // if (numMoved > 0) 440 // System.arraycopy(_array, index, _array, index + numNew, 441 // numMoved); 442 443 // System.arraycopy(a, 0, _array, index, numNew); 444 // size += numNew; 445 // return numNew != 0; 446 // } 447 448 /** 449 * Removes from this list all of the elements whose index is between 450 * {@code fromIndex}, inclusive, and {@code toIndex}, exclusive. 451 * Shifts any succeeding elements to the left (reduces their index). 452 * This call shortens the list by {@code (toIndex - fromIndex)} elements. 453 * (If {@code toIndex==fromIndex}, this operation has no effect.) 454 * 455 * @throws IndexOutOfBoundsException if {@code fromIndex} or 456 * {@code toIndex} is out of range 457 * ({@code fromIndex < 0 || 458 * fromIndex >= size() || 459 * toIndex > size() || 460 * toIndex < fromIndex}) 461 */ 462 protected void removeRange(int fromIndex, int toIndex) { 463 _array.linearRemove(_array[fromIndex..toIndex]); 464 } 465 466 /** 467 * Checks if the given index is in range. If not, throws an appropriate 468 * runtime exception. This method does *not* check if the index is 469 * negative: It is always used immediately prior to an array access, 470 * which throws an ArrayIndexOutOfBoundsException if index is negative. 471 */ 472 private void rangeCheck(int index) { 473 if (index >= _array.length) 474 throw new IndexOutOfBoundsException(outOfBoundsMsg(index)); 475 } 476 477 /** 478 * A version of rangeCheck used by add and addAll. 479 */ 480 private void rangeCheckForAdd(int index) { 481 if (index > _array.length || index < 0) 482 throw new IndexOutOfBoundsException(outOfBoundsMsg(index)); 483 } 484 485 /** 486 * Constructs an IndexOutOfBoundsException detail message. 487 * Of the many possible refactorings of the error handling code, 488 * this "outlining" performs best with both server and client VMs. 489 */ 490 private string outOfBoundsMsg(int index) { 491 return "Index: "~index.to!string()~", Size: " ~ to!string(size()); 492 } 493 494 /** 495 * Removes from this list all of its elements that are contained in the 496 * specified collection. 497 * 498 * @param c collection containing elements to be removed from this list 499 * @return {@code true} if this list changed as a result of the call 500 * @throws ClassCastException if the class of an element of this list 501 * is incompatible with the specified collection 502 * (<a href="Collection.html#optional-restrictions">optional</a>) 503 * @throws NullPointerException if this list contains a null element and the 504 * specified collection does not permit null elements 505 * (<a href="Collection.html#optional-restrictions">optional</a>), 506 * or if the specified collection is null 507 * @see Collection#contains(Object) 508 */ 509 // bool removeAll(Collection<?> c) { 510 // Objects.requireNonNull(c); 511 // return batchRemove(c, false); 512 // } 513 514 /** 515 * Retains only the elements in this list that are contained in the 516 * specified collection. In other words, removes from this list all 517 * of its elements that are not contained in the specified collection. 518 * 519 * @param c collection containing elements to be retained in this list 520 * @return {@code true} if this list changed as a result of the call 521 * @throws ClassCastException if the class of an element of this list 522 * is incompatible with the specified collection 523 * (<a href="Collection.html#optional-restrictions">optional</a>) 524 * @throws NullPointerException if this list contains a null element and the 525 * specified collection does not permit null elements 526 * (<a href="Collection.html#optional-restrictions">optional</a>), 527 * or if the specified collection is null 528 * @see Collection#contains(Object) 529 */ 530 // bool retainAll(Collection!E c) { 531 // assert(c !is null); 532 // // return batchRemove(c, true); 533 534 // _array[].remove!(x => !c.canFind(x)); 535 // return true; 536 // } 537 538 // private bool batchRemove(Collection<?> c, bool complement) { 539 // final Object[] _array = this._array; 540 // int r = 0, w = 0; 541 // bool modified = false; 542 // try { 543 // for (; r < size; r++) 544 // if (c.contains(_array[r]) == complement) 545 // _array[w++] = _array[r]; 546 // } finally { 547 // // Preserve behavioral compatibility with AbstractCollection, 548 // // even if c.contains() throws. 549 // if (r != size) { 550 // System.arraycopy(_array, r, 551 // _array, w, 552 // size - r); 553 // w += size - r; 554 // } 555 // if (w != size) { 556 // // clear to let GC do its work 557 // for (int i = w; i < size; i++) 558 // _array[i] = null; 559 // modCount += size - w; 560 // size = w; 561 // modified = true; 562 // } 563 // } 564 // return modified; 565 // } 566 567 /** 568 * Save the state of the <tt>ArrayList</tt> instance to a stream (that 569 * is, serialize it). 570 * 571 * @serialData The length of the array backing the <tt>ArrayList</tt> 572 * instance is emitted (int), followed by all of its elements 573 * (each an <tt>Object</tt>) in the proper order. 574 */ 575 // private void writeObject(java.io.ObjectOutputStream s) 576 // throws java.io.IOException{ 577 // // Write out element count, and any hidden stuff 578 // int expectedModCount = modCount; 579 // s.defaultWriteObject(); 580 581 // // Write out size as capacity for behavioural compatibility with clone() 582 // s.writeInt(size); 583 584 // // Write out all elements in the proper order. 585 // for (int i=0; i<size; i++) { 586 // s.writeObject(_array[i]); 587 // } 588 589 // if (modCount != expectedModCount) { 590 // throw new ConcurrentModificationException(); 591 // } 592 // } 593 594 /** 595 * Reconstitute the <tt>ArrayList</tt> instance from a stream (that is, 596 * deserialize it). 597 */ 598 // private void readObject(java.io.ObjectInputStream s) 599 // throws java.io.IOException, ClassNotFoundException { 600 // _array = EMPTY_ELEMENTDATA; 601 602 // // Read in size, and any hidden stuff 603 // s.defaultReadObject(); 604 605 // // Read in capacity 606 // s.readInt(); // ignored 607 608 // if (size > 0) { 609 // // be like clone(), allocate array based upon size not capacity 610 // ensureCapacityInternal(size); 611 612 // Object[] a = _array; 613 // // Read in all elements in the proper order. 614 // for (int i=0; i<size; i++) { 615 // a[i] = s.readObject(); 616 // } 617 // } 618 // } 619 620 621 /** 622 * Returns a view of the portion of this list between the specified 623 * {@code fromIndex}, inclusive, and {@code toIndex}, exclusive. (If 624 * {@code fromIndex} and {@code toIndex} are equal, the returned list is 625 * empty.) The returned list is backed by this list, so non-structural 626 * changes in the returned list are reflected in this list, and vice-versa. 627 * The returned list supports all of the optional list operations. 628 * 629 * <p>This method eliminates the need for explicit range operations (of 630 * the sort that commonly exist for arrays). Any operation that expects 631 * a list can be used as a range operation by passing a subList view 632 * instead of a whole list. For example, the following idiom 633 * removes a range of elements from a list: 634 * <pre> 635 * list.subList(from, to).clear(); 636 * </pre> 637 * Similar idioms may be constructed for {@link #indexOf(Object)} and 638 * {@link #lastIndexOf(Object)}, and all of the algorithms in the 639 * {@link Collections} class can be applied to a subList. 640 * 641 * <p>The semantics of the list returned by this method become undefined if 642 * the backing list (i.e., this list) is <i>structurally modified</i> in 643 * any way other than via the returned list. (Structural modifications are 644 * those that change the size of this list, or otherwise perturb it in such 645 * a fashion that iterations in progress may yield incorrect results.) 646 * 647 * @throws IndexOutOfBoundsException {@inheritDoc} 648 * @throws Exception {@inheritDoc} 649 */ 650 // List<E> subList(int fromIndex, int toIndex) { 651 // subListRangeCheck(fromIndex, toIndex, size); 652 // return new SubList(this, 0, fromIndex, toIndex); 653 // } 654 655 // static void subListRangeCheck(int fromIndex, int toIndex, int size) { 656 // if (fromIndex < 0) 657 // throw new IndexOutOfBoundsException("fromIndex = " ~ fromIndex); 658 // if (toIndex > size) 659 // throw new IndexOutOfBoundsException("toIndex = " ~ toIndex); 660 // if (fromIndex > toIndex) 661 // throw new Exception("fromIndex(" ~ fromIndex + 662 // ") > toIndex(" ~ toIndex ~ ")"); 663 // } 664 665 static if (isOrderingComparable!E) { 666 override void sort(bool isAscending = true) { 667 668 // https://issues.dlang.org/show_bug.cgi?id=15304 669 // std.algorithm.sort(_array[]); 670 671 int expectedModCount = modCount; 672 if(isAscending) 673 std.algorithm.sort!(lessThan!E)(_array[]); 674 else 675 std.algorithm.sort!(greaterthan!E)(_array[]); 676 677 if (modCount != expectedModCount) 678 throw new ConcurrentModificationException(); 679 modCount++; 680 } 681 } 682 683 684 override void sort(Comparator!E c) { 685 int expectedModCount = modCount; 686 std.algorithm.sort!((a, b) => c.compare(a, b) < 0)(_array[]); 687 688 if (modCount != expectedModCount) 689 throw new ConcurrentModificationException(); 690 modCount++; 691 } 692 693 694 /** 695 * Returns an iterator over the elements in this list in proper sequence. 696 * 697 * <p>The returned iterator is <a href="#fail-fast"><i>fail-fast</i></a>. 698 * 699 * @return an iterator over the elements in this list in proper sequence 700 */ 701 override InputRange!E iterator() { 702 return new Itr(); 703 } 704 705 /** 706 * An optimized version of AbstractList.Itr 707 */ 708 private class Itr : InputRange!E { 709 int cursor; // index of next element to return 710 int lastRet = -1; // index of last element returned; -1 if no such 711 int expectedModCount; 712 713 // prevent creating a synthetic constructor 714 this() { 715 expectedModCount = this.outer.modCount; 716 } 717 718 bool empty() { 719 return cursor == size(); 720 } 721 722 E front() { 723 checkForComodification(); 724 int i = cursor; 725 if (i >= size()) 726 throw new NoSuchElementException(); 727 return this.outer._array[lastRet = i]; 728 } 729 730 void popFront() { 731 int i = cursor; 732 if (i >= size()) 733 throw new NoSuchElementException(); 734 cursor = i + 1; 735 } 736 737 E moveFront() { 738 // this.outer._array.moveFront(); 739 throw new NotImplementedException(); 740 } 741 742 // void remove() { 743 // if (lastRet < 0) 744 // throw new IllegalStateException(); 745 // checkForComodification(); 746 747 // try { 748 // ArrayList.this.remove(lastRet); 749 // cursor = lastRet; 750 // lastRet = -1; 751 // expectedModCount = modCount; 752 // } catch (IndexOutOfBoundsException ex) { 753 // throw new ConcurrentModificationException(); 754 // } 755 // } 756 757 // void forEachRemaining(Consumer!E action) { 758 // Objects.requireNonNull(action); 759 // int size = ArrayList.this.size; 760 // int i = cursor; 761 // if (i < size) { 762 // Object[] es = elementData; 763 // if (i >= es.length) 764 // throw new ConcurrentModificationException(); 765 // for (; i < size && modCount == expectedModCount; i++) 766 // action.accept(elementAt(es, i)); 767 // // update once at end to reduce heap write traffic 768 // cursor = i; 769 // lastRet = i - 1; 770 // checkForComodification(); 771 // } 772 // } 773 774 int opApply(scope int delegate(E) dg) { 775 int result = 0; 776 foreach(ref E e; this.outer._array) { 777 result = dg(e); 778 } 779 return result; 780 } 781 782 /// Ditto 783 int opApply(scope int delegate(size_t, E) dg) { 784 int result = 0; 785 size_t index = 0; 786 foreach(ref E e; this.outer._array) { 787 result = dg(index++, e); 788 } 789 return result; 790 } 791 792 final void checkForComodification() { 793 if (modCount != expectedModCount) 794 throw new ConcurrentModificationException(); 795 } 796 } 797 } 798 799 /** 800 * Lazy List creation. 801 * <p> 802 * A List helper class that attempts to avoid unnecessary List creation. If a 803 * method needs to create a List to return, but it is expected that this will 804 * either be empty or frequently contain a single item, then using LazyList will 805 * avoid additional object creations by using {@link Collections#EMPTY_LIST} or 806 * {@link Collections#singletonList(Object)} where possible. 807 * </p> 808 * <p> 809 * LazyList works by passing an opaque representation of the list in and out of 810 * all the LazyList methods. This opaque object is either null for an empty 811 * list, an Object for a list with a single entry or an {@link ArrayList} for a 812 * list of items. 813 * </p> 814 * <strong>Usage</strong> 815 * 816 * <pre> 817 * Object lazylist = null; 818 * while (loopCondition) { 819 * Object item = getItem(); 820 * if (item.isToBeAdded()) 821 * lazylist = LazyList.add(lazylist, item); 822 * } 823 * return LazyList.getList(lazylist); 824 * </pre> 825 * 826 * An ArrayList of default size is used as the initial LazyList. 827 * 828 * @see java.util.List 829 */ 830 class LazyList 831 { 832 // this(){ 833 834 // } 835 836 /** 837 * Add an item to a LazyList 838 * 839 * @param list 840 * The list to add to or null if none yet created. 841 * @param item 842 * The item to add. 843 * @return The lazylist created or added to. 844 */ 845 static Object add(Object list, Object item) { 846 if (list is null) { 847 if (typeid(item) == typeid(List!Object) || item is null) { 848 List!Object l = new ArrayList!Object(); 849 l.add(item); 850 return cast(Object)l; 851 } 852 853 return item; 854 } 855 856 if (typeid(list) == typeid(List!Object)) { 857 (cast(List!Object) list).add(item); 858 return list; 859 } 860 861 List!Object l = new ArrayList!Object(); 862 l.add(list); 863 l.add(item); 864 return cast(Object)l; 865 } 866 867 /** 868 * Get the real List from a LazyList. 869 * 870 * @param list 871 * A LazyList returned from LazyList.add(Object) or null 872 * @param nullForEmpty 873 * If true, null is returned instead of an empty list. 874 * @return The List of added items, which may be null, an EMPTY_LIST or a 875 * SingletonList. 876 * @param <E> 877 * the list entry type 878 */ 879 static List!E getList(E)(Object list, bool nullForEmpty) { 880 if (list is null) { 881 if (nullForEmpty) 882 return null; 883 return new EmptyList!E(); // Collections.emptyList(); 884 } 885 List!E r = cast(List!E) list; 886 if (r !is null) 887 return r; 888 889 // return (List<E>) Collections.singletonList(list); 890 auto l = new ArrayList!E(); 891 l.add(cast(E)list); 892 return l; 893 } 894 895 896 /** 897 * The size of a lazy List 898 * 899 * @param list 900 * A LazyList returned from LazyList.add(Object) or null 901 * @return the size of the list. 902 */ 903 static int size(T)(List!(T) list) { 904 if (list is null) 905 return 0; 906 return list.size(); 907 } 908 909 }