Reputation: 1242
In Scala, is there a built-in function or external library for concatenating two lists (or arrays, or vectors, or listbuffers, etc) in constant time? Such an operation would presumably destroy / mutate the two original lists. All the functions I see for concatenating lists run in linear time, as far as I can tell.
Thanks much.
Upvotes: 12
Views: 2851
Reputation: 32335
There is the UnrolledBuffer
which has the concat
method taking another UnrolledBuffer
and returning their concatenation in O(1)
. It is destructive to the argument buffer - the second buffer will be empty after this calling this method.
Upvotes: 13
Reputation: 137957
The classic (going back to at least Hughes '84) approach in functional languages to solve constant-time append in is via "difference lists", where appending to the list is encoded as function composition.
Here's a sketch in Haskell:
newtype DList a = DL { unDL :: [a] -> [a] }
So a DList is a function from lists to lists. Some introduction forms:
-- The empty list is the identity function
empty = DL id
-- Singletons are the compositions of the cons function
singleton = DL . (:)
-- Appending two lists is just composition
append xs ys = DL (unDL xs . unDL ys)
The full implementation is on Hackage, and should be trivial to translate to Scala.
Upvotes: 2
Reputation: 2147
I was thinking that a DoubleLinkedList
could offer a constant-time append, since you could join the end of one list to the beginning of another without having to traverse either one.
However, neither the scala.collections.mutable.DoubleLinkedList
or java.util.List
work that way.
The reason is probably that it would mean a.append(b)
would modify both a and b, which would be unexpected.
Upvotes: 1
Reputation: 4475
Here is a simple immutable data structure that supports constant time concatenation. It just shows that it is possible, but is not intended for practical use. The items
implementation to retrieve the elements has a pretty bad run-time and could easily be improved by walking the tree with an iterator.
I'm wondering if there are any better data structures?
sealed abstract class Tree[+T] {
def items: List[T]
def append[U >: T](v: U): Tree[U] = this append Leave(v)
def append[U >: T](other: Tree[U]) = Node(this, other)
}
case class Node[+T](val left: Tree[T], val right: Tree[T]) extends Tree[T] {
def items = left.items ++ right.items
}
case class Leave[+T](val value: T) extends Tree[T] {
def items = List(value)
}
case object EmptyTree extends Tree[Nothing] {
def items = Nil
}
object ConstantTimeConcatenation {
def main(args: Array[String]) {
val first = EmptyTree append 1 append 2 append 3
val second = EmptyTree append 4 append 5
val both = first append second // runs in linear time
println(both.items)
}
}
Upvotes: 0