If I understood correctly, you store each individual pixel in one Stream
element, and this can be inefficient. What you can do is create your custom LazyRaster
class which contains lazy references to blocks of the image of some size (for instance, 20x20). The first time some block is written, its corresponding array is initialized, and from there on changing a pixel means writing to that array.
This is more work, but may result in better performance. Furthermore, if you wish to support stacking of image operations (e.g. do a map - take - map), and then evaluating the image in "one-go", the implementation could get tricky - stream implementation is the best evidence for this.
Another thing one can do is ensure that the old Stream
s are being properly garbage collected. I suspect image
object in your example is a wrapper for your streams. If you wish to stack multiple image operations (like mapping) together and be able to gc the references you no longer need, you have to make sure that you don't hold any references to a stream - note that this is not ensured if:
- you have a reference to your image on the stack (
image
in the example)
- your
Image
wrapper contains such a reference.
Without knowing more about the exact use cases, its hard to say more.
Personally, I would avoid Stream
s altogether, and simply use some immutable array-based data structure which is both space-efficient and avoids boxing. The only place where I potentially see Stream
s being used is in iterative image transformations, like convolution or applying a stack of filters. You wouldn't have a Stream
of pixels, but a Stream
of images, instead. This could be a nice way to express a sequence of transformations - in this case, the comments about gc in the link given above apply.
Best Answer
First, they are all non-strict. That has a particular mathematical meaning related to functions, but, basically, means they are computed on-demand instead of in advance.
Stream
is a lazy list indeed. In fact, in Scala, aStream
is aList
whosetail
is alazy val
. Once computed, a value stays computed and is reused. Or, as you say, the values are cached.An
Iterator
can only be used once because it is a traversal pointer into a collection, and not a collection in itself. What makes it special in Scala is the fact that you can apply transformation such asmap
andfilter
and simply get a newIterator
which will only apply these transformations when you ask for the next element.Scala used to provide iterators which could be reset, but that is very hard to support in a general manner, and they didn't make version 2.8.0.
Views are meant to be viewed much like a database view. It is a series of transformation which one applies to a collection to produce a "virtual" collection. As you said, all transformations are re-applied each time you need to fetch elements from it.
Both
Iterator
and views have excellent memory characteristics.Stream
is nice, but, in Scala, its main benefit is writing infinite sequences (particularly sequences recursively defined). One can avoid keeping all of theStream
in memory, though, by making sure you don't keep a reference to itshead
(for example, by usingdef
instead ofval
to define theStream
).Because of the penalties incurred by views, one should usually
force
it after applying the transformations, or keep it as a view if only few elements are expected to ever be fetched, compared to the total size of the view.