Reputation: 3906
There are several ways to construct an immutable list in Scala (see contrived example code below). You can use a mutable ListBuffer, create a var
list and modify it, use a tail recursive method, and probably others that I don't know about.
Instinctively, I use the ListBuffer, but I don't have a good reason for doing so. Is there a preferred or idiomatic method for creating a list, or are there situations that are best for one method over another?
import scala.collection.mutable.ListBuffer
// THESE are all the same as: 0 to 3 toList.
def listTestA() ={
var list:List[Int] = Nil
for(i <- 0 to 3)
list = list ::: List(i)
list
}
def listTestB() ={
val list = new ListBuffer[Int]()
for (i <- 0 to 3)
list += i
list.toList
}
def listTestC() ={
def _add(l:List[Int], i:Int):List[Int] = i match {
case 3 => l ::: List(3)
case _ => _add(l ::: List(i), i +1)
}
_add(Nil, 0)
}
Upvotes: 123
Views: 118026
Reputation: 8481
just an example that uses collection.breakOut
scala> val a : List[Int] = (for( x <- 1 to 10 ) yield x * 3)(collection.breakOut)
a: List[Int] = List(3, 6, 9, 12, 15, 18, 21, 24, 27, 30)
scala> val b : List[Int] = (1 to 10).map(_ * 3)(collection.breakOut)
b: List[Int] = List(3, 6, 9, 12, 15, 18, 21, 24, 27, 30)
Upvotes: 0
Reputation: 15537
Note: This answer is written for an old version of Scala.
The Scala collection classes are going to be redesigned as of Scala 2.8, so be prepared to change the way you create lists very soon.
What is the forward compatible way of creating a List? I have no idea since I haven't read the 2.8 docs yet.
A PDF document describing the proposed changes of the collection classes
Upvotes: 2
Reputation: 20415
Using List.tabulate
, like this,
List.tabulate(3)( x => 2*x )
res: List(0, 2, 4)
List.tabulate(3)( _ => Math.random )
res: List(0.935455779102479, 0.6004888906328091, 0.3425278797788426)
List.tabulate(3)( _ => (Math.random*10).toInt )
res: List(8, 0, 7)
Upvotes: 2
Reputation: 591
As a new scala developer i wrote small test to check list creation time with suggested methods above. It looks like (for ( p <- ( 0 to x ) ) yield p) toList the fastest approach.
import java.util.Date
object Listbm {
final val listSize = 1048576
final val iterationCounts = 5
def getCurrentTime: BigInt = (new Date) getTime
def createList[T] ( f : Int => T )( size : Int ): T = f ( size )
// returns function time execution
def experiment[T] ( f : Int => T ) ( iterations: Int ) ( size :Int ) : Int = {
val start_time = getCurrentTime
for ( p <- 0 to iterations ) createList ( f ) ( size )
return (getCurrentTime - start_time) toInt
}
def printResult ( f: => Int ) : Unit = println ( "execution time " + f )
def main( args : Array[String] ) {
args(0) match {
case "for" => printResult ( experiment ( x => (for ( p <- ( 0 to x ) ) yield p) toList ) ( iterationCounts ) ( listSize ) )
case "range" => printResult ( experiment ( x => ( 0 to x ) toList ) ( iterationCounts ) ( listSize ) )
case "::" => printResult ( experiment ( x => ((0 to x) :\ List[Int]())(_ :: _) ) ( iterationCounts ) ( listSize ) )
case _ => println ( "please use: for, range or ::\n")
}
}
}
Upvotes: 1
Reputation: 5962
You want to focus on immutability in Scala generally by eliminating any vars. Readability is still important for your fellow man so:
Try:
scala> val list = for(i <- 1 to 10) yield i
list: scala.collection.immutable.IndexedSeq[Int] = Vector(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
You probably don't even need to convert to a list in most cases :)
The indexed seq will have everything you need:
That is, you can now work on that IndexedSeq:
scala> list.foldLeft(0)(_+_)
res0: Int = 55
Upvotes: 5
Reputation: 11596
I always prefer List and I use "fold/reduce" before "for comprehension". However, "for comprehension" is preferred if nested "folds" are required. Recursion is the last resort if I can not accomplish the task using "fold/reduce/for".
so for your example, I will do:
((0 to 3) :\ List[Int]())(_ :: _)
before I do:
(for (x <- 0 to 3) yield x).toList
Note: I use "foldRight(:\)" instead of "foldLeft(/:)" here because of the order of "_"s. For a version that does not throw StackOverflowException, use "foldLeft" instead.
Upvotes: 2
Reputation: 13221
Uhmm.. these seem too complex to me. May I propose
def listTestD = (0 to 3).toList
or
def listTestE = for (i <- (0 to 3).toList) yield i
Upvotes: 24
Reputation: 297185
ListBuffer
is a mutable list which has constant-time append, and constant-time conversion into a List
.
List
is immutable and has constant-time prepend and linear-time append.
How you construct your list depends on the algorithm you'll use the list for and the order in which you get the elements to create it.
For instance, if you get the elements in the opposite order of when they are going to be used, then you can just use a List
and do prepends. Whether you'll do so with a tail-recursive function, foldLeft
, or something else is not really relevant.
If you get the elements in the same order you use them, then a ListBuffer
is most likely a preferable choice, if performance is critical.
But, if you are not on a critical path and the input is low enough, you can always reverse
the list later, or just foldRight
, or reverse
the input, which is linear-time.
What you DON'T do is use a List
and append to it. This will give you much worse performance than just prepending and reversing at the end.
Upvotes: 112