
org.opalj.collection.immutable.RefArray.scala Maven / Gradle / Ivy
The newest version!
/* BSD 2-Clause License - see OPAL/LICENSE for details. */
package org.opalj
package collection
package immutable
import java.util.{Arrays ⇒ JArrays}
import scala.collection.Map
import scala.collection.mutable.Builder
import org.opalj.collection.mutable.RefArrayBuffer
import org.opalj.control.{find ⇒ findInArray}
/**
* Wraps an array such that the underlying array is no longer directly accessible and
* therefore also no longer mutable if `RefArray` is the sole owner.
*
* @note Compared to `ConstArray`, `RefArray` does not provide an efficient
* `toArray` method. However, `RefArray`s are covariant.
*
* @author Michael Eichberg
*/
class RefArray[+T /* "<: AnyRef" this constraint is ONLY enforced by the factory methods to facilitate integration with the scala collection API */ ] private[collection] (
private var data: Array[AnyRef]
) extends scala.collection.immutable.Seq[T] { self ⇒ // TODO [Scala 2.13] make it extend IndexedSeq.
//
//
// UNSAFE OPERATIONS THAT SHOULD ONLY BE USED AT CONSTRUCTION TIME
//
//
/**
* Appends the given element to the underlying array; will cause havoc if this object
* is not under full control of the caller.
*/
// IMPROVE Design annotation (+Analysis) that ensures that this operation is only performed if – after the usage of this method - the reference to this data-structure will not be used anymore.
def _UNSAFE_added[X >: T <: AnyRef](elem: X): RefArray[X] = {
val newData = JArrays.copyOf(data, data.length + 1)
newData(data.length) = elem
data = newData
this.asInstanceOf[RefArray[X]]
}
// IMPROVE Design annotation (+Analysis) that ensures that this operation is only performed if – after the usage of this method - the reference to this data-structure will not be used anymore.
def _UNSAFE_addedAll[X >: T <: AnyRef](that: RefArray[X]): RefArray[X] = {
val newData = JArrays.copyOf(data, this.data.length + that.data.length)
System.arraycopy(that.data, 0, newData, this.data.length, that.data.length)
data = newData
this.asInstanceOf[RefArray[X]]
}
/**
* Directly performs the map operation on the underlying array and then creates a new
* appropriately typed `RefArray[X]` object which wraps the modified array. Hence, the return
* value can be ignored, if `X == T`.
*
* '''This method is only to be used if no aliases have been created that assume that this array is not mutated.'''
*/
// IMPROVE Design annotation (+Analysis) that ensures that this operation is only performed if – after the usage of this method - the reference to this data-structure will not be used anymore.
def _UNSAFE_mapped[X <: AnyRef](f: T ⇒ X): RefArray[X] = {
var i = 0
val max = data.length
while (i < max) {
data(i) = f(data(i).asInstanceOf[T])
i += 1
}
new RefArray[X](data)
}
/**
* Directly performs the sort operation on the underlying array.
*
* '''This method is only to be used if `this` instance is no longer used afterwards!'''
*/
// IMPROVE Design annotation (+Analysis) that ensures that this operation is only performed if – after the usage of this method - the reference to this data-structure will not be used anymore.
def _UNSAFE_sortedWith(compare: (T, T) ⇒ Boolean): this.type = {
JArrays.parallelSort[AnyRef](
data,
Ordering.fromLessThan(compare).asInstanceOf[java.util.Comparator[AnyRef]]
)
this
}
/**
* Directly updates the value at the given index and then creates a new
* appropriately typed `RefArray[X]` object which wraps the modified array. The returned
* type can be ignored if the type X == T.
*
* '''This method is only to be used if `this` instance is no longer used afterwards!'''
*/
// IMPROVE Design annotation (+Analysis) that ensures that this operation is only performed if – after the usage of this method - the reference to this data-structure will not be used anymore.
def _UNSAFE_replaced[X >: T <: AnyRef](index: Int, e: X): RefArray[X] = {
data(index) = e
new RefArray(data)
}
/**
* Returns a new RefArray where the values are sorted based on their natural ordering.
*
* @example
* {{{
* RefArray("c","a").sorted[String]
* }}}
*/
def _UNSAFE_sorted[X >: T](implicit ev: T <:< Comparable[X]): this.type = {
ignore(ev) // <= HACK... should have no effect at runtime.
JArrays.parallelSort[AnyRef](data, null)
this
}
//
//
// SAFE OPERATIONS THAT DO NOT MANIPULATE THE DATA-STRUCTURE IN PLACE
//
//
/*
class WithFilter {
def withFilter(p: T ⇒ Boolean): WithFilter
def map
def flatMap
def foreach
}
def withFilter(p: T ⇒ Boolean): WithFilter = {
???
}
*/
override def dropRight(n: Int): RefArray[T] = {
if (n == 0)
return this;
val newLength = data.length - n
if (newLength <= 0)
return RefArray.empty;
new RefArray(JArrays.copyOf(data, newLength))
}
def ++[X >: T <: AnyRef](that: RefArray[X]): RefArray[X] = {
val newData = JArrays.copyOf(data, this.data.length + that.data.length)
System.arraycopy(that.data, 0, newData, this.data.length, that.data.length)
new RefArray(newData)
}
def ++[X >: T <: AnyRef](that: Seq[X]): RefArray[X] = {
val thisLength = this.data.length
val newData = JArrays.copyOf(data, thisLength + that.length)
var i = thisLength
that.foreach { v ⇒
newData(i) = v
i += 1
}
new RefArray(newData)
}
def +:[X >: T <: AnyRef](e: X): RefArray[X] = {
val thisLength = this.data.length
val newData = new Array[AnyRef](thisLength + 1)
newData(0) = e
System.arraycopy(this.data, 0, newData, 1, thisLength)
new RefArray(newData)
}
def map[X <: AnyRef](f: T ⇒ X): RefArray[X] = {
val newData = new Array[AnyRef](data.length)
var i = 0
val max = data.length
while (i < max) {
newData(i) = f(data(i).asInstanceOf[T])
i += 1
}
new RefArray[X](newData)
}
def map(f: T ⇒ Int): IntArray = {
val newData = new Array[Int](data.length)
var i = 0
val max = data.length
while (i < max) {
newData(i) = f(data(i).asInstanceOf[T])
i += 1
}
IntArray._UNSAFE_from(newData)
}
def flatMap[X <: AnyRef](f: T ⇒ TraversableOnce[X]): RefArray[X] = {
val b = RefArray.newBuilder[X]
var i = 0
val max = data.length
b.sizeHint(max)
while (i < max) {
b ++= f(data(i).asInstanceOf[T])
i += 1
}
b.result()
}
def forallEquals(v: AnyRef): Boolean = {
var i = 0
val max = data.length
while (i < max) {
if (v != data(i)) return false;
i += 1
}
true
}
def apply(idx: Int): T = data(idx).asInstanceOf[T]
def sortWith[X >: T](compare: (X, X) ⇒ Boolean): RefArray[T] = {
val newData = data.clone
JArrays.parallelSort(newData, Ordering.fromLessThan(compare).asInstanceOf[Ordering[AnyRef]])
new RefArray[T](newData)
}
/** The following method is "just" required to shut-up the compiler. */
@inline final private[this] def ignore(x: AnyRef): AnyRef = x
/**
* Returns a new RefArray where the values are sorted based on their natural ordering.
*
* @example
* {{{
* RefArray("c","a").sorted[String]
* }}}
*/
@inline final def sorted[X >: T](implicit ev: T <:< Comparable[X]): RefArray[T] = {
ignore(ev) // <= HACK... should have no effect at runtime.
val newData = data.clone
JArrays.parallelSort[AnyRef](newData, null)
new RefArray[T](newData)
}
/**
* Checks if the given element is stored in the array. Performs a linear sweep of the array
* (complexity O(N)).
* If the array happens to be sorted, consider using `binarySearch`.
*/
override def contains[X >: T](v: X): Boolean = {
val data = this.data
val max = data.length
var i = 0
while (i < max) {
if (data(i) == v) {
return true;
}
i += 1
}
false
}
override def isEmpty: Boolean = data.length == 0
override def nonEmpty: Boolean = data.length > 0
override def size: Int = data.length
def length: Int = data.length
override def head: T = {
if (nonEmpty) {
data(0).asInstanceOf[T]
} else {
throw new NoSuchElementException
}
}
/**
* Computes a slice.
*
* @param from The index of the first element (inclusive)
* @param until The index of the last element (exclusive); if the last element is beyond the
* size of the underlying data-structure, null values will be added.
* @return The sliced array.
*/
override def slice(from: Int, until: Int): RefArray[T] = {
new RefArray(JArrays.copyOfRange(data, from, until))
}
override def tail: RefArray[T] = {
new RefArray(JArrays.copyOfRange(data, 1, data.length))
}
override def foreach[U](f: T ⇒ U): Unit = {
val data = this.data
val max = data.length
var i = 0
while (i < max) {
f(data(i).asInstanceOf[T])
i += 1
}
}
override def partition(p: T ⇒ Boolean): (RefArray[T], RefArray[T]) = {
val max = data.length
val left = RefArrayBuffer.withInitialSize[AnyRef](Math.max(8, max / 2))
val right = RefArrayBuffer.withInitialSize[AnyRef](Math.min(8, max / 2))
var i = 0
while (i < max) {
val e = data(i)
if (p(e.asInstanceOf[T])) {
left += e
} else {
right += e
}
i += 1
}
(new RefArray(left.toArray), new RefArray(right.toArray))
}
def partitionByType[X <: AnyRef](clazz: Class[X]): (RefArray[X], RefArray[T]) = {
val max = data.length
val left = RefArrayBuffer.withInitialSize[AnyRef](Math.max(8, max / 2))
val right = RefArrayBuffer.withInitialSize[AnyRef](Math.min(8, max / 2))
var i = 0
while (i < max) {
val e = data(i)
if (clazz.isInstance(e)) {
left += e
} else {
right += e
}
i += 1
}
(new RefArray[X](left.toArray), new RefArray[T](right.toArray))
}
/** Appends the given element. */
def :+[X >: T <: AnyRef](elem: X): RefArray[X] = {
val newData = JArrays.copyOf(data, data.length + 1)
newData(data.length) = elem
new RefArray[X](newData)
}
override def iterator: RefIterator[T] = new RefIterator[T] {
private[this] var i = 0
override def hasNext: Boolean = i < data.length
override def next(): T = { val e = data(i).asInstanceOf[T]; i += 1; e }
}
def foreachIterator: ForeachRefIterator[T] = new ForeachRefIterator[T] {
override def foreach[U](f: T ⇒ U): Unit = {
val data = self.data
val max = data.length
var i = 0
while (i < max) {
f(data(i).asInstanceOf[T])
i += 1
}
}
}
override def filter(f: T ⇒ Boolean): RefArray[T] = {
// IMPROVE Only create new array if required!
val b = RefArray.newUnconstrainedBuilder[T]
val data = this.data
val max = data.length
var c = 0 // counts the number of filtered (retained) values
b.sizeHint(Math.min(8, max))
var i = 0
while (i < max) {
val e = data(i).asInstanceOf[T]
if (f(e)) {
b += e
c += 1
}
i += 1
}
if (c == max)
this
else
b.result()
}
def foldLeft(z: Int)(op: (Int, T) ⇒ Int): Int = iterator.foldLeft(z)(op)
override def foldLeft[X](z: X)(op: (X, T) ⇒ X): X = {
var result = z
var i = 0
val max = data.length
while (i < max) {
result = op(result, data(i).asInstanceOf[T])
i += 1
}
result
}
override def headOption: Option[T] = if (size > 0) Some(data(0).asInstanceOf[T]) else None
/**
* Creates a view which represents the slice with the elements with the indexes [from,until).
*
* @param from The first value (inclusive!)
* @param until The last value (exclusive!)
*/
def slicedView(from: Int, until: Int = data.length): RefIndexedView[T] = new RefIndexedView[T] {
override def apply(index: Int): T = self.data(from + index).asInstanceOf[T]
override def isEmpty: Boolean = from == until
override def size: Int = until - from
override def iterator: RefIterator[T] = new RefIterator[T] {
private[this] var index: Int = from
override def hasNext: Boolean = index < until
override def next(): T = { val e = data(index); index += 1; e.asInstanceOf[T] }
}
}
def filterNonNull: RefArray[T] = {
var b: Builder[T, RefArray[T]] = null // we initialize the builder only on demand
val data = this.data
val max = data.length
var i = 0
while (i < max) {
val e = data(i)
if (e == null) {
// let's ignore "e"
if (b == null) {
b = RefArray.newUnconstrainedBuilder[T]
b.sizeHint(max - 1)
var p = 0
while (p < i) {
b += data(p).asInstanceOf[T]
p += 1
}
}
} else if (b != null) {
b += e.asInstanceOf[T]
}
i += 1
}
if (b == null)
this
else
b.result()
}
override def filterNot(f: T ⇒ Boolean): RefArray[T] = filter(e ⇒ !f(e))
/**
* Creates a new `RefArray` where the value at the given index is replaced by
* the given value.
*/
def updated[X >: T](index: Int, e: X): RefArray[X] = {
val newData = java.util.Arrays.copyOf(data, data.length)
newData(index) = e.asInstanceOf[AnyRef]
new RefArray(newData)
}
def binarySearch(comparator: T ⇒ Int): Option[T] = {
findInArray(data)(e ⇒ comparator(e.asInstanceOf[T])).asInstanceOf[Option[T]]
}
def binarySearch[X >: T <: Comparable[X]](key: X): Int = {
JArrays.binarySearch(data, 0, data.length, key.asInstanceOf[Object])
}
/**
* Creates a new `RefArray` where the given value is inserted at the specified
* `insertionPoint`. If the underlying array happens to be sorted, then the insertion point can
* easily be computed using `binarySearch`; it will be `-index -1` if the
* returned index is less than zero; otherwise the value was already found in the array.
*/
def insertedAt[X >: T <: AnyRef](insertionPoint: Int, e: X): RefArray[X] = {
val newData = JArrays.copyOf(data, data.length + 1)
newData(insertionPoint) = e
System.arraycopy(data, insertionPoint, newData, insertionPoint + 1, data.length - insertionPoint)
new RefArray(newData)
}
def zipWithIndex: RefArray[(T, Int)] = {
val max = data.length
val newData = new Array[AnyRef](max)
var i = 0
while (i < max) {
newData(i) = (data(i), i)
i += 1
}
new RefArray(newData)
}
def foreachWithIndex[U](f: (T, Int) ⇒ U): Unit = {
val data = self.data
val max = data.length
var i = 0
while (i < max) {
f(data(i).asInstanceOf[T], i)
i += 1
}
}
def sum(f: T ⇒ Int): Int = {
var sum = 0
val data = self.data
val max = data.length
var i = 0
while (i < max) {
sum += f(data(i).asInstanceOf[T])
i += 1
}
sum
}
override def equals(other: Any): Boolean = {
other match {
case that: RefArray[_] ⇒ JArrays.equals(this.data, that.data)
case _ ⇒ false
}
}
override lazy val hashCode: Int = JArrays.hashCode(data) * 11
override def toString: String = data.mkString("RefArray(", ", ", ")")
}
/**
* Factory for [[RefArray]]s.
*/
object RefArray {
/**
* @note We can't enforce the type bound `<: AnyRef` here, because the builder is also
* used by `RefArray` where we can't enforce the type bound without giving
* up the the advantageous of inheriting from the standard scala collection types.
* However, the type bound is still in place and `T` is expected to be a subtype of
* `AnyRef`.
*/
private[collection] def newUnconstrainedBuilder[T]: Builder[T, RefArray[T]] = {
new Builder[T, RefArray[T]] {
private[this] var data: Array[AnyRef] = null
private[this] var nextIndex = 0
override def +=(e: T): this.type = {
if (data == null) {
data = new Array[AnyRef](8)
} else if (nextIndex == data.length) {
data = JArrays.copyOf(data, (nextIndex + 1) * 2)
}
data(nextIndex) = e.asInstanceOf[AnyRef]
nextIndex += 1
this
}
override def clear(): Unit = nextIndex = 0
override def result(): RefArray[T] = {
if (data == null)
Empty
else {
new RefArray[T](
if (nextIndex == data.length) data else JArrays.copyOf(data, nextIndex)
)
}
}
override def sizeHint(size: Int): Unit = {
if (data == null) data = new Array[AnyRef](size)
}
}
}
def newBuilder[T <: AnyRef]: Builder[T, RefArray[T]] = newUnconstrainedBuilder[T]
val Empty: RefArray[Nothing] = new RefArray(new Array[AnyRef](0))
def empty[T <: AnyRef]: RefArray[T] = Empty.asInstanceOf[RefArray[T]]
def apply[T <: AnyRef](e: T): RefArray[T] = new RefArray(Array[AnyRef](e))
def apply[T <: AnyRef](e1: T, e2: T): RefArray[T] = new RefArray(Array[AnyRef](e1, e2))
def apply[T <: AnyRef](e1: T, e2: T, e3: T): RefArray[T] = {
new RefArray(Array[AnyRef](e1, e2, e3))
}
def fill[T <: AnyRef](n: Int)(f: ⇒ T): RefArray[T] = {
val data = new Array[AnyRef](n)
var i = 0
while (i < n) {
data(i) = f
i += 1
}
new RefArray[T](data)
}
def apply[T <: AnyRef](e1: T, e2: T, e3: T, data: T*): RefArray[T] = {
val max = data.length
val newData = new Array[AnyRef](data.length + 3)
newData(0) = e1
newData(1) = e2
newData(2) = e3
var i = 0
while (i < max) {
newData(i + 3) = data(i)
i += 1
}
new RefArray(newData)
}
def unapplySeq[T <: AnyRef](x: RefArray[T]): Option[Seq[T]] = Some(x)
def withSize[T <: AnyRef](size: Int): RefArray[T] = new RefArray(new Array[AnyRef](size))
/**
* Creates a new [[RefArray]] by cloning the given array.
*
* I.e., modifications to the given array will not be reflected.
*/
def from[T <: AnyRef](data: Array[AnyRef]): RefArray[T] = {
new RefArray(data.clone())
}
def from[T <: AnyRef](take: Int, it: Iterator[AnyRef]): RefArray[T] = {
val data = new Array[AnyRef](take)
var i = 0
while (i < take && it.hasNext) {
data(i) = it.next()
i += 1
}
new RefArray(data)
}
def from[X <: AnyRef, Y <: AnyRef](map: Map[X, Y]): RefArray[(X, Y)] = {
val b = newBuilder[(X, Y)]
b.sizeHint(map.size)
map.foreach(b.+=)
b.result()
}
def mapFrom[T, X <: AnyRef](data: Seq[T])(f: T ⇒ X): RefArray[X] = {
val max = data.size
val newData = new Array[AnyRef](max)
var i = 0; while (i < max) { newData(i) = f(data(i)); i += 1 }
new RefArray[X](newData)
}
def mapFrom[T <: AnyRef](data: Array[Int])(f: Int ⇒ T): RefArray[T] = {
val max = data.length
val newData = new Array[AnyRef](max)
var i = 0; while (i < max) { newData(i) = f(data(i)); i += 1 }
new RefArray[T](newData)
}
/**
* Creates a new [[RefArray]] from the given array. Hence, changes to the
* underlying array would be reflected!
*
* '''Only use this factory method if you have full control over all
* aliases to the given array to ensure that the underlying array is not mutated.'''
*/
// IMPROVE Use an ownership annotation to specify that RefArray takes over the ownership of the array.
def _UNSAFE_from[T <: AnyRef](data: Array[AnyRef]): RefArray[T] = new RefArray(data)
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy