All Downloads are FREE. Search and download functionalities are using the official Maven repository.

scalax.collection.mutable.SimpleArraySet.scala Maven / Gradle / Ivy

The newest version!
package scalax.collection
package mutable

import scala.collection.mutable.{ExtHashSet, GrowableBuilder}
import scala.collection.{IterableFactory, IterableFactoryDefaults, SortedSet, StrictOptimizedIterableOps}
import scala.util.Random

import scalax.collection.immutable.SortedArraySet

/** A basic [[ArraySet]] implementation suitable for efficient add operations.
  * Element removal could be optimized by another implementation.
  *
  * @param hints Optimization hints controlling the growth of the underlying
  *        [[scala.collection.mutable.ArrayBuffer!]].
  * @define OPT Optimized by use of unchecked insertions.
  * @author Peter Empen
  */
@SerialVersionUID(1L)
final class SimpleArraySet[A](override val hints: ArraySet.Hints)
    extends ArraySet[A]
    with StrictOptimizedIterableOps[A, SimpleArraySet, SimpleArraySet[A]]
    with IterableFactoryDefaults[A, SimpleArraySet]
    with Serializable {

  override def iterableFactory = SimpleArraySet

  protected[collection] def newNonCheckingBuilder[B] = new SimpleArraySet.NonCheckingBuilder[A, B](this)
  override def clone                                 = (newNonCheckingBuilder ++= this).result()
  private var nextFree: Int                          = 0
  private var arr: Array[A]                          = _
  private var hashSet: ExtHashSet[A]                 = _

  private def initialize(): Unit = {
    val capacity = hints.nextCapacity(0)
    if (capacity == 0) hashSet = ExtHashSet.empty[A]
    else arr = new Array[AnyRef](capacity).asInstanceOf[Array[A]]
  }
  initialize()

  def capacity: Int                   = if (isHash) 0 else arr.length
  @inline private def isHash: Boolean = arr eq null
  @inline def isArray: Boolean        = !isHash
  protected[collection] def array     = arr
  protected[collection] def set       = hashSet

  def addOne(elem: A) = { add(elem); this }

  def subtractOne(elem: A) = {
    if (isHash) hashSet -= elem
    else removeIndex(indexOf(elem))
    this
  }

  protected def removeIndex(i: Int): Unit =
    if (i != -1) {
      if (i + 1 < nextFree)
        java.lang.System.arraycopy(arr, i + 1, arr, i, nextFree - i - 1)
      nextFree -= 1
    }

  protected[collection] def +=!(elem: A): this.type = {
    if (isHash) hashSet add elem
    else {
      if (nextFree == capacity)
        if (resizedToHash) {
          add(elem); return this
        }
      arr(nextFree) = elem
      nextFree += 1
    }
    this
  }

  protected[collection] def map(xs: IterableOnce[A]): this.type = this

  override def iterator: Iterator[A] =
    if (isHash) hashSet.iterator
    else
      new scala.collection.AbstractIterator[A] {
        private[this] var i          = 0
        private[this] var prevElm: A = _
        def hasNext =
          i < nextFree
        def next() = {
          if (i >= nextFree)
            throw new NoSuchElementException
          prevElm = arr(i)
          i += 1
          prevElm
        }
      }

  override def foreach[U](f: (A) => U): Unit =
    if (isHash) hashSet foreach f
    else {
      var i = 0
      while (i < nextFree) { f(arr(i)); i += 1 }
    }

  protected def resizeArray(fromCapacity: Int, toCapacity: Int): Unit = {
    val newArr: Array[AnyRef] = new Array(toCapacity)
    java.lang.System.arraycopy(arr, 0, newArr, 0, math.min(fromCapacity, toCapacity))
    arr = newArr.asInstanceOf[Array[A]]
  }

  protected def setToArray(set: Iterable[A], size: Int): Unit = {
    arr = new Array[AnyRef](size).asInstanceOf[Array[A]]
    nextFree = 0
    set foreach { elem =>
      arr(nextFree) = elem
      nextFree += 1
    }
    hashSet = null
  }

  def compact(): Unit =
    if (isHash) {
      val _size = size
      if (_size < hints.hashTableThreshold)
        setToArray(hashSet, _size)
    } else if (
      hints.compactUpToUsed match {
        case perc if perc == 0   => false
        case perc if perc == 100 => nextFree < capacity
        case perc                => perc >= nextFree * 100 / capacity
      }
    )
      resizeArray(capacity, nextFree)

  protected def indexOf[B](elem: B, pred: (A, B) => Boolean): Int = {
    var i = 0
    while (i < nextFree)
      if (pred(arr(i), elem)) return i
      else i += 1
    -1
  }

  /* Optimized 'arr contains c'. */
  protected def indexOf(elem: A): Int = {
    var i = 0
    while (i < nextFree)
      if (arr(i) == elem) return i
      else i += 1
    -1
  }

  override def contains(elem: A): Boolean =
    if (isHash) hashSet contains elem
    else indexOf(elem) >= 0

  def find(elem: A): Option[A] =
    if (isHash) hashSet find (_ == elem)
    else {
      val i = indexOf(elem)
      if (i >= 0) Some(arr(i)) else None
    }

  override def add(elem: A): Boolean =
    if (isHash) hashSet add elem
    else {
      if (nextFree == capacity)
        if (resizedToHash)
          return add(elem)
      var i = 0
      while (i < nextFree)
        if (arr(i) == elem) return false
        else i += 1
      arr(nextFree) = elem
      nextFree += 1
      true
    }

  protected def resizedToHash: Boolean = {
    val newCapacity = hints.nextCapacity(capacity)
    if (newCapacity == 0) {
      hashSet = ExtHashSet.empty[A]
      hashSet sizeHint capacity
      hashSet ++= iterator
      arr = null
      true
    } else {
      resizeArray(capacity, newCapacity)
      false
    }
  }

  override def size = if (isHash) hashSet.size else nextFree

  protected[collection] def upsert(elem: A with AnyRef): Boolean =
    if (isHash) hashSet upsert elem
    else {
      val i        = indexOf(elem)
      val isUpdate = i >= 0
      if (isUpdate) arr(i) = elem
      else add(elem)
      !isUpdate
    }

  /** $OPT */
  override def filter(p: (A) => Boolean) =
    if (isHash) super.filter(p)
    else {
      val b = newNonCheckingBuilder[A]
      for (x <- this)
        if (p(x)) b += x
      b.result()
    }

  /** Faster mapping in case the caller ensures to insert no duplicates. */
  protected[collection] def mapUnchecked[B, That](f: A => B): SimpleArraySet[B] =
    if (isHash) super.map(f)
    else {
      val b = newNonCheckingBuilder[B]
      for (x <- this) b += f(x)
      b.result()
    }

  /** $OPT */
  override def partition(p: A => Boolean) =
    if (isHash) super.partition(p)
    else {
      val l, r = newNonCheckingBuilder[A]
      for (x <- this) (if (p(x)) l else r) += x
      (l.result(), r.result())
    }

  def sorted(implicit ord: Ordering[A]): SortedSet[A] =
    if (isHash) {
      SortedSet.from(hashSet)
    } else {
      val newArr: Array[AnyRef] = new Array(nextFree)
      java.lang.System.arraycopy(arr, 0, newArr, 0, nextFree)
      new SortedArraySet(newArr.asInstanceOf[Array[A]])
    }

  def findElem[B](other: B, correspond: (A, B) => Boolean): A =
    if (isHash) hashSet findElem (other, correspond)
    else {
      val idx = indexOf(other, (a: A, b: B) => a.hashCode == b.hashCode && correspond(a, b))
      (if (idx < 0) null else arr(idx)).asInstanceOf[A]
    }

  def draw(random: Random): A =
    if (isHash) hashSet draw random
    else arr(random.nextInt(size))

  override def clear(): Unit =
    for (elem <- this.toList)
      this -= elem
}

/** @define FROM The [[ArraySet]] instance an operation of which this builder is invoked on.
  */
object SimpleArraySet extends IterableFactory[SimpleArraySet] {

  /** Returns an empty set with default hints. */
  override def empty[A]: SimpleArraySet[A] = new SimpleArraySet[A](ArraySet.Hints())

  /** Returns an empty set with custom hints. */
  def emptyWithHints[A](implicit hints: ArraySet.Hints): SimpleArraySet[A] =
    new SimpleArraySet[A](hints)

  /** Returns an empty set with hints propagated from `arraySet`. */
  def emptyWithPropagatedHints[A, B](arraySet: ArraySet[A]): SimpleArraySet[B] =
    emptyWithHints(arraySet.hints.propagate(arraySet.size))

  /** Default `ArraySet` builder preventing duplicates. The hints passed are propagated
    * such that `initial size == from.size`.
    *
    * @param from $FROM
    */
  protected class CheckingBuilder[A](from: ArraySet[A])
      extends GrowableBuilder[A, SimpleArraySet[A]](emptyWithPropagatedHints(from))

  /** An `ArraySet` builder without duplicate checking.
    *
    * @param from $FROM
    */
  protected class NonCheckingBuilder[A, B](from: ArraySet[A])
      extends GrowableBuilder[B, SimpleArraySet[B]](emptyWithPropagatedHints[A, B](from)) {
    override def addOne(x: B): this.type = { elems +=! x; this }
  }

  override def from[A](source: IterableOnce[A]) = empty ++= source
  override def newBuilder[A]                    = new GrowableBuilder[A, SimpleArraySet[A]](empty)
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy