Reputation: 6039
Suppose I have a case class
for a certain purpose.
case class SamplePair(
arrayOfNumbers: ArrayBuffer[Int]
)
Suppose I have sequenceOfSamplePair
have a Seq
of our basic SamplePair
, and I want to add some ordered numbers to the arrayOfNumbers
element of each element.
One way is doing this:
val ctr = new AtomicInteger(0)
sequenceOfSamplePair.foreach{ entry =>
entry.arrayOfNumbers += ctr.getAndIncrement()
}
Another way is doing this, using zipWithIndex
.
sequenceOfSamplePair.zipWithIndex.foreach {
case (entry, ctr) =>
entry.arrayOfNumbers += ctr
}
What would be the reasons to prefer the implementation using zipWithIndex
? Is it more "functional"?
Upvotes: 1
Views: 523
Reputation: 11508
I've just done some profiling with these two methods. There's no clear winner. If the pattern matching in the zipWithIndex
is replaced with direct references (_1
and _2
) then it's slightly faster but still not a clear winner.
If you however use streaming like the following:
Stream.range(0, sequenceOfSamplePair.length).map { i =>
sequenceOfSamplePair(i) += i
}
then, it's about 6x faster. That's because it is really very simple, just go through the counter one by one, directly adding to the number. 6x is a lot faster, meaning it seems that getAndIncrement
makes it very, very slow in relative terms.
Upvotes: 2
Reputation: 10884
As both of your approaches are not functional i'd go with the first implementation. In the second approach you mix a functional construct with a imperative construct whereas you loose all gains you could get out of a functional solution.
Also using a mutable buffer in the case class goes not well with a mutable Buffer.
a more functional approach could look like
case class SamplePair(numbers: List[Int])
val extendedSequenceOfSamplePair = sequenceOfSamplePair.zipWithIndex.map {
case ( sp@SamplePair(oldNumbers), idx) => sp.copy( numbers = idx :: oldNumbers)
}
Upvotes: 2