From a02245f76f4ec3d4bb9b7cd81069b6c6d19a0a66 Mon Sep 17 00:00:00 2001 From: Julien Richard-Foy Date: Tue, 25 Jul 2017 11:19:53 +0200 Subject: [PATCH 1/3] Fix mutable.HashMap.getOrElseUpdate. --- .../strawman/collection/mutable/HashMap.scala | 8 +++++--- .../strawman/collection/mutable/HashTable.scala | 15 +++++++++++---- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/src/main/scala/strawman/collection/mutable/HashMap.scala b/src/main/scala/strawman/collection/mutable/HashMap.scala index a7449c626c..e25818a080 100644 --- a/src/main/scala/strawman/collection/mutable/HashMap.scala +++ b/src/main/scala/strawman/collection/mutable/HashMap.scala @@ -89,12 +89,14 @@ final class HashMap[K, V] private[collection] (contents: HashTable.Contents[K, D val entry = table.findEntry0(key, i) if (entry != null) entry.value else { - val table0 = table + val table0 = table.table val default = defaultValue // Avoid recomputing index if the `defaultValue()` hasn't triggered // a table resize. - val newEntryIndex = if (table0 eq table) i else table.index(hash) - table.addEntry0(table.createNewEntry(key, default), newEntryIndex) + val newEntryIndex = if (table0 eq table.table) i else table.index(hash) + val e = table.createNewEntry(key, default) + if (table.tableSize >= table.threshold) table.addEntry(e) + else table.addEntry2(e, newEntryIndex) default } } diff --git a/src/main/scala/strawman/collection/mutable/HashTable.scala b/src/main/scala/strawman/collection/mutable/HashTable.scala index b77baeb24d..db07eeaddc 100644 --- a/src/main/scala/strawman/collection/mutable/HashTable.scala +++ b/src/main/scala/strawman/collection/mutable/HashTable.scala @@ -49,17 +49,17 @@ private[mutable] abstract class HashTable[A, B, Entry >: Null <: HashEntry[A, En /** The actual hash table. */ - @transient protected var table: Array[HashEntry[A, Entry]] = new Array(initialCapacity) + @transient protected[collection] var table: Array[HashEntry[A, Entry]] = new Array(initialCapacity) /** The number of mappings contained in this hash table. */ - @transient protected var tableSize: Int = 0 + @transient protected[collection] var tableSize: Int = 0 final def size: Int = tableSize /** The next size value at which to resize (capacity * load factor). */ - @transient protected var threshold: Int = initialThreshold(_loadFactor) + @transient protected[collection] var threshold: Int = initialThreshold(_loadFactor) /** The array keeping track of the number of elements in 32 element blocks. */ @@ -148,7 +148,7 @@ private[mutable] abstract class HashTable[A, B, Entry >: Null <: HashEntry[A, En /** Add entry to table * pre: no entry with same key exists */ - protected final def addEntry(e: Entry): Unit = { + protected[collection] final def addEntry(e: Entry): Unit = { addEntry0(e, index(elemHashCode(e.key))) } @@ -161,6 +161,13 @@ private[mutable] abstract class HashTable[A, B, Entry >: Null <: HashEntry[A, En resize(2 * table.length) } + protected[collection] def addEntry2(e: Entry, h: Int): Unit = { + e.next = table(h).asInstanceOf[Entry] + table(h) = e + tableSize += 1 + nnSizeMapAdd(h) + } + /** Find entry with given key in table, or add new one if not found. * May be somewhat faster then `findEntry`/`addEntry` pair as it * computes entry's hash index only once. From 8d85d9cf0efbaeac9171a0191e73ddddad355320 Mon Sep 17 00:00:00 2001 From: Julien Richard-Foy Date: Tue, 25 Jul 2017 16:38:56 +0200 Subject: [PATCH 2/3] Add missing methods on iterators. Optimized concat. --- .../scala/strawman/collection/Iterator.scala | 227 ++++++++++++++++-- 1 file changed, 211 insertions(+), 16 deletions(-) diff --git a/src/main/scala/strawman/collection/Iterator.scala b/src/main/scala/strawman/collection/Iterator.scala index 6aac2628f5..b95c9bf83c 100644 --- a/src/main/scala/strawman/collection/Iterator.scala +++ b/src/main/scala/strawman/collection/Iterator.scala @@ -1,10 +1,14 @@ package strawman.collection import java.util.concurrent.atomic.{AtomicInteger, AtomicReference} -import scala.{Any, Array, Boolean, Int, math, None, NoSuchElementException, Nothing, Option, StringContext, Some, Unit, throws} + +import scala.{Any, Array, Boolean, IllegalArgumentException, Int, NoSuchElementException, None, Nothing, Option, Some, StringContext, Unit, `inline`, math, throws} import scala.Predef.{intWrapper, require} import strawman.collection.mutable.ArrayBuffer +import scala.annotation.tailrec +import scala.annotation.unchecked.uncheckedVariance + /** A core Iterator class * * @define consumesIterator @@ -17,6 +21,13 @@ trait Iterator[+A] extends IterableOnce[A] { self => def next(): A def iterator() = this + /** Tests whether this iterator is empty. + * + * @return `true` if hasNext is false, `false` otherwise. + * @note Reuse: $preservesIterator + */ + def isEmpty: Boolean = !hasNext + /** Tests whether a predicate holds for all values produced by this iterator. * $mayNotTerminateInf * @@ -45,6 +56,16 @@ trait Iterator[+A] extends IterableOnce[A] { self => res } + /** Tests whether this iterator contains a given value as an element. + * $mayNotTerminateInf + * + * @param elem the element to test. + * @return `true` if this iterator produces some value that is + * is equal (as determined by `==`) to `elem`, `false` otherwise. + * @note Reuse: $consumesIterator + */ + def contains(elem: Any): Boolean = exists(_ == elem) // Note--this seems faster than manual inlining! + /** Counts the number of elements in the $coll which satisfy a predicate. * * @param p the predicate used to test elements. @@ -303,7 +324,7 @@ trait Iterator[+A] extends IterableOnce[A] { self => def foreach[U](f: A => U): Unit = while (hasNext) f(next()) - def indexWhere(p: A => Boolean, from: Int): Int = { + def indexWhere(p: A => Boolean, from: Int = 0): Int = { var i = math.max(from, 0) drop(from) while (hasNext) { @@ -313,12 +334,50 @@ trait Iterator[+A] extends IterableOnce[A] { self => -1 } - def length = { + /** Returns the index of the first occurrence of the specified + * object in this iterable object. + * $mayNotTerminateInf + * + * @param elem element to search for. + * @return the index of the first occurrence of `elem` in the values produced by this iterator, + * or -1 if such an element does not exist until the end of the iterator is reached. + * @note Reuse: $consumesIterator + */ + def indexOf[B >: A](elem: B): Int = indexOf(elem, 0) + + /** Returns the index of the first occurrence of the specified object in this iterable object + * after or at some start index. + * $mayNotTerminateInf + * + * @param elem element to search for. + * @param from the start index + * @return the index `>= from` of the first occurrence of `elem` in the values produced by this + * iterator, or -1 if such an element does not exist until the end of the iterator is + * reached. + * @note Reuse: $consumesIterator + */ + def indexOf[B >: A](elem: B, from: Int): Int = { + var i = 0 + while (i < from && hasNext) { + next() + i += 1 + } + + while (hasNext) { + if (next() == elem) return i + i += 1 + } + -1 + } + + def length: Int = { var len = 0 while (hasNext) { len += 1; next() } len } + final def size: Int = length + def filter(p: A => Boolean): Iterator[A] = new Iterator[A] { private var hd: A = _ private var hdDefined: Boolean = false @@ -356,19 +415,9 @@ trait Iterator[+A] extends IterableOnce[A] { self => def next() = current.next() } - def ++[B >: A](xs: IterableOnce[B]): Iterator[B] = new Iterator[B] { - private var myCurrent: Iterator[B] = self - private var first = true - private def current = { - if (!myCurrent.hasNext && first) { - myCurrent = xs.iterator() - first = false - } - myCurrent - } - def hasNext = current.hasNext - def next() = current.next() - } + def concat[B >: A](xs: => IterableOnce[B]): Iterator[B] = new Iterator.ConcatIterator[B](self).concat(xs) + + @`inline` def ++ [B >: A](xs: => IterableOnce[B]): Iterator[B] = concat(xs) def take(n: Int): Iterator[A] = new Iterator[A] { private var i = 0 @@ -502,4 +551,150 @@ object Iterator { def apply(n: Int) = xs(n) }.iterator() + /** Creates an infinite-length iterator which returns successive values from some start value. + + * @param start the start value of the iterator + * @return the iterator producing the infinite sequence of values `start, start + 1, start + 2, ...` + */ + def from(start: Int): Iterator[Int] = from(start, 1) + + /** Creates an infinite-length iterator returning values equally spaced apart. + * + * @param start the start value of the iterator + * @param step the increment between successive values + * @return the iterator producing the infinite sequence of values `start, start + 1 * step, start + 2 * step, ...` + */ + def from(start: Int, step: Int): Iterator[Int] = new Iterator[Int] { + private var i = start + def hasNext: Boolean = true + def next(): Int = { val result = i; i += step; result } + } + + /** Creates nn iterator returning successive values in some integer interval. + * + * @param start the start value of the iterator + * @param end the end value of the iterator (the first value NOT returned) + * @return the iterator producing values `start, start + 1, ..., end - 1` + */ + def range(start: Int, end: Int): Iterator[Int] = range(start, end, 1) + + /** An iterator producing equally spaced values in some integer interval. + * + * @param start the start value of the iterator + * @param end the end value of the iterator (the first value NOT returned) + * @param step the increment value of the iterator (must be positive or negative) + * @return the iterator producing values `start, start + step, ...` up to, but excluding `end` + */ + def range(start: Int, end: Int, step: Int): Iterator[Int] = new Iterator[Int] { + if (step == 0) throw new IllegalArgumentException("zero step") + private var i = start + def hasNext: Boolean = (step <= 0 || i < end) && (step >= 0 || i > end) + def next(): Int = + if (hasNext) { val result = i; i += step; result } + else empty.next() + } + + /** Creates an infinite iterator that repeatedly applies a given function to the previous result. + * + * @param start the start value of the iterator + * @param f the function that's repeatedly applied + * @return the iterator producing the infinite sequence of values `start, f(start), f(f(start)), ...` + */ + def iterate[T](start: T)(f: T => T): Iterator[T] = new Iterator[T] { + private[this] var first = true + private[this] var acc = start + def hasNext: Boolean = true + def next(): T = { + if (first) first = false + else acc = f(acc) + + acc + } + } + + /** Creates an infinite-length iterator returning the results of evaluating an expression. + * The expression is recomputed for every element. + * + * @param elem the element computation. + * @return the iterator containing an infinite number of results of evaluating `elem`. + */ + def continually[A](elem: => A): Iterator[A] = new Iterator[A] { + def hasNext = true + def next = elem + } + + /** Creates an iterator to which other iterators can be appended efficiently. + * Nested ConcatIterators are merged to avoid blowing the stack. + */ + private final class ConcatIterator[+A](private var current: Iterator[A @uncheckedVariance]) extends Iterator[A] { + private var tail: ConcatIteratorCell[A @uncheckedVariance] = null + private var last: ConcatIteratorCell[A @uncheckedVariance] = null + private var currentHasNextChecked = false + + // Advance current to the next non-empty iterator + // current is set to null when all iterators are exhausted + @tailrec + private[this] def advance(): Boolean = { + if (tail eq null) { + current = null + last = null + false + } + else { + current = tail.headIterator + tail = tail.tail + merge() + if (currentHasNextChecked) true + else if (current.hasNext) { + currentHasNextChecked = true + true + } else advance() + } + } + + // If the current iterator is a ConcatIterator, merge it into this one + @tailrec + private[this] def merge(): Unit = + if (current.isInstanceOf[ConcatIterator[_]]) { + val c = current.asInstanceOf[ConcatIterator[A]] + current = c.current + currentHasNextChecked = c.currentHasNextChecked + if (c.tail ne null) { + c.last.tail = tail + tail = c.tail + } + merge() + } + + def hasNext = + if (currentHasNextChecked) true + else if (current eq null) false + else if (current.hasNext) { + currentHasNextChecked = true + true + } else advance() + + def next() = + if (hasNext) { + currentHasNextChecked = false + current.next() + } else Iterator.empty.next() + + override def concat[B >: A](that: => IterableOnce[B]): Iterator[B] = { + val c = new ConcatIteratorCell[B](that, null).asInstanceOf[ConcatIteratorCell[A]] + if(tail eq null) { + tail = c + last = c + } else { + last.tail = c + last = c + } + if(current eq null) current = Iterator.empty + this + } + } + + private[this] final class ConcatIteratorCell[A](head: => IterableOnce[A], var tail: ConcatIteratorCell[A]) { + def headIterator: Iterator[A] = head.iterator() + } } From 660aca66322df5a24efb632000058a65f4470e24 Mon Sep 17 00:00:00 2001 From: Julien Richard-Foy Date: Thu, 27 Jul 2017 11:28:32 +0200 Subject: [PATCH 3/3] Fix parens for dotty --- src/main/scala/strawman/collection/Iterator.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/scala/strawman/collection/Iterator.scala b/src/main/scala/strawman/collection/Iterator.scala index b95c9bf83c..b035587baa 100644 --- a/src/main/scala/strawman/collection/Iterator.scala +++ b/src/main/scala/strawman/collection/Iterator.scala @@ -620,7 +620,7 @@ object Iterator { */ def continually[A](elem: => A): Iterator[A] = new Iterator[A] { def hasNext = true - def next = elem + def next() = elem } /** Creates an iterator to which other iterators can be appended efficiently.