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