Skip to content

Commit a93ad1a

Browse files
flomebulbishabosha
andauthored
Add code tabs for collections-2.13/sets, immutabes and muttabes (#2572)
* Add code tabs for collections-2.13/sets * Small corrections proposed to collections-2.13/maps: remove '"' that look like unnecessary. * Add code tabs for collections-2.13/concrete-immutable-collection-classes * Add code tabs for collections-2.13/concrete-mutable-collection-classes * adjust indentation Co-authored-by: Jamie Thompson <bishbashboshjt@gmail.com>
1 parent 9f19404 commit a93ad1a

File tree

4 files changed

+413
-179
lines changed

4 files changed

+413
-179
lines changed

_overviews/collections-2.13/concrete-immutable-collection-classes.md

+151-57
Original file line numberDiff line numberDiff line change
@@ -24,25 +24,42 @@ A [LazyList](https://www.scala-lang.org/api/{{ site.scala-version }}/scala/colle
2424

2525
Whereas lists are constructed with the `::` operator, lazy lists are constructed with the similar-looking `#::`. Here is a simple example of a lazy list containing the integers 1, 2, and 3:
2626

27-
scala> val lazyList = 1 #:: 2 #:: 3 #:: LazyList.empty
28-
lazyList: scala.collection.immutable.LazyList[Int] = LazyList(<not computed>)
27+
{% tabs LazyList_1 %}
28+
{% tab 'Scala 2 and 3' for=LazyList_1 %}
29+
~~~scala
30+
scala> val lazyList = 1 #:: 2 #:: 3 #:: LazyList.empty
31+
lazyList: scala.collection.immutable.LazyList[Int] = LazyList(<not computed>)
32+
~~~
33+
{% endtab %}
34+
{% endtabs %}
2935

3036
The head of this lazy list is 1, and the tail of it has 2 and 3. None of the elements are printed here, though, because the list
3137
hasn’t been computed yet! Lazy lists are specified to compute lazily, and the `toString` method of a lazy list is careful not to force any extra evaluation.
3238

3339
Below is a more complex example. It computes a lazy list that contains a Fibonacci sequence starting with the given two numbers. A Fibonacci sequence is one where each element is the sum of the previous two elements in the series.
3440

35-
36-
scala> def fibFrom(a: Int, b: Int): LazyList[Int] = a #:: fibFrom(b, a + b)
37-
fibFrom: (a: Int,b: Int)LazyList[Int]
41+
{% tabs LazyList_2 %}
42+
{% tab 'Scala 2 and 3' for=LazyList_2 %}
43+
~~~scala
44+
scala> def fibFrom(a: Int, b: Int): LazyList[Int] = a #:: fibFrom(b, a + b)
45+
fibFrom: (a: Int,b: Int)LazyList[Int]
46+
~~~
47+
{% endtab %}
48+
{% endtabs %}
3849

3950
This function is deceptively simple. The first element of the sequence is clearly `a`, and the rest of the sequence is the Fibonacci sequence starting with `b` followed by `a + b`. The tricky part is computing this sequence without causing an infinite recursion. If the function used `::` instead of `#::`, then every call to the function would result in another call, thus causing an infinite recursion. Since it uses `#::`, though, the right-hand side is not evaluated until it is requested.
4051
Here are the first few elements of the Fibonacci sequence starting with two ones:
4152

42-
scala> val fibs = fibFrom(1, 1).take(7)
43-
fibs: scala.collection.immutable.LazyList[Int] = LazyList(<not computed>)
44-
scala> fibs.toList
45-
res9: List[Int] = List(1, 1, 2, 3, 5, 8, 13)
53+
{% tabs LazyList_3 %}
54+
{% tab 'Scala 2 and 3' for=LazyList_3 %}
55+
~~~scala
56+
scala> val fibs = fibFrom(1, 1).take(7)
57+
fibs: scala.collection.immutable.LazyList[Int] = LazyList(<not computed>)
58+
scala> fibs.toList
59+
res9: List[Int] = List(1, 1, 2, 3, 5, 8, 13)
60+
~~~
61+
{% endtab %}
62+
{% endtabs %}
4663

4764
## Immutable ArraySeqs
4865

@@ -56,24 +73,32 @@ and thus they can be much more convenient to write.
5673

5774
ArraySeqs are built and updated just like any other sequence.
5875

59-
~~~
76+
{% tabs ArraySeq_1 %}
77+
{% tab 'Scala 2 and 3' for=ArraySeq_1 %}
78+
~~~scala
6079
scala> val arr = scala.collection.immutable.ArraySeq(1, 2, 3)
6180
arr: scala.collection.immutable.ArraySeq[Int] = ArraySeq(1, 2, 3)
6281
scala> val arr2 = arr :+ 4
6382
arr2: scala.collection.immutable.ArraySeq[Int] = ArraySeq(1, 2, 3, 4)
6483
scala> arr2(0)
6584
res22: Int = 1
6685
~~~
86+
{% endtab %}
87+
{% endtabs %}
6788

6889
ArraySeqs are immutable, so you cannot change an element in place. However, the `updated`, `appended` and `prepended`
6990
operations create new ArraySeqs that differ from a given ArraySeq only in a single element:
7091

71-
~~~
92+
{% tabs ArraySeq_2 %}
93+
{% tab 'Scala 2 and 3' for=ArraySeq_2 %}
94+
~~~scala
7295
scala> arr.updated(2, 4)
7396
res26: scala.collection.immutable.ArraySeq[Int] = ArraySeq(1, 2, 4)
7497
scala> arr
7598
res27: scala.collection.immutable.ArraySeq[Int] = ArraySeq(1, 2, 3)
7699
~~~
100+
{% endtab %}
101+
{% endtabs %}
77102

78103
As the last line above shows, a call to `updated` has no effect on the original ArraySeq `arr`.
79104

@@ -91,67 +116,115 @@ but linear for `ArraySeq`, and, conversely, indexed access is constant for `Arra
91116

92117
Vectors are built and modified just like any other sequence.
93118

94-
scala> val vec = scala.collection.immutable.Vector.empty
95-
vec: scala.collection.immutable.Vector[Nothing] = Vector()
96-
scala> val vec2 = vec :+ 1 :+ 2
97-
vec2: scala.collection.immutable.Vector[Int] = Vector(1, 2)
98-
scala> val vec3 = 100 +: vec2
99-
vec3: scala.collection.immutable.Vector[Int] = Vector(100, 1, 2)
100-
scala> vec3(0)
101-
res1: Int = 100
119+
{% tabs Vector_1 %}
120+
{% tab 'Scala 2 and 3' for=Vector_1 %}
121+
~~~scala
122+
scala> val vec = scala.collection.immutable.Vector.empty
123+
vec: scala.collection.immutable.Vector[Nothing] = Vector()
124+
scala> val vec2 = vec :+ 1 :+ 2
125+
vec2: scala.collection.immutable.Vector[Int] = Vector(1, 2)
126+
scala> val vec3 = 100 +: vec2
127+
vec3: scala.collection.immutable.Vector[Int] = Vector(100, 1, 2)
128+
scala> vec3(0)
129+
res1: Int = 100
130+
~~~
131+
{% endtab %}
132+
{% endtabs %}
102133

103-
Vectors are represented as trees with a high branching factor. (The branching factor of a tree or a graph is the number of children at each node.) The details of how this is accomplished [changed](https://github.com/scala/scala/pull/8534) in Scala 2.13.2, but the basic idea remains the same, as follows.
134+
Vectors are represented as trees with a high branching factor (The branching factor of a tree or a graph is the number of children at each node). The details of how this is accomplished [changed](https://github.com/scala/scala/pull/8534) in Scala 2.13.2, but the basic idea remains the same, as follows.
104135

105136
Every tree node contains up to 32 elements of the vector or contains up to 32 other tree nodes. Vectors with up to 32 elements can be represented in a single node. Vectors with up to `32 * 32 = 1024` elements can be represented with a single indirection. Two hops from the root of the tree to the final element node are sufficient for vectors with up to 2<sup>15</sup> elements, three hops for vectors with 2<sup>20</sup>, four hops for vectors with 2<sup>25</sup> elements and five hops for vectors with up to 2<sup>30</sup> elements. So for all vectors of reasonable size, an element selection involves up to 5 primitive array selections. This is what we meant when we wrote that element access is "effectively constant time".
106137

107138
Like selection, functional vector updates are also "effectively constant time". Updating an element in the middle of a vector can be done by copying the node that contains the element, and every node that points to it, starting from the root of the tree. This means that a functional update creates between one and five nodes that each contain up to 32 elements or subtrees. This is certainly more expensive than an in-place update in a mutable array, but still a lot cheaper than copying the whole vector.
108139

109140
Because vectors strike a good balance between fast random selections and fast random functional updates, they are currently the default implementation of immutable indexed sequences:
110141

111-
scala> collection.immutable.IndexedSeq(1, 2, 3)
112-
res2: scala.collection.immutable.IndexedSeq[Int] = Vector(1, 2, 3)
142+
{% tabs Vector_2 %}
143+
{% tab 'Scala 2 and 3' for=Vector_2 %}
144+
~~~scala
145+
scala> collection.immutable.IndexedSeq(1, 2, 3)
146+
res2: scala.collection.immutable.IndexedSeq[Int] = Vector(1, 2, 3)
147+
~~~
148+
{% endtab %}
149+
{% endtabs %}
113150

114151
## Immutable Queues
115152

116153
A [Queue](https://www.scala-lang.org/api/{{ site.scala-version }}/scala/collection/immutable/Queue.html) is a first-in-first-out sequence. You enqueue an element onto a queue with `enqueue`, and dequeue an element with `dequeue`. These operations are constant time.
117154

118155
Here's how you can create an empty immutable queue:
119156

120-
scala> val empty = scala.collection.immutable.Queue[Int]()
121-
empty: scala.collection.immutable.Queue[Int] = Queue()
157+
{% tabs Queue_1 %}
158+
{% tab 'Scala 2 and 3' for=Queue_1 %}
159+
~~~scala
160+
scala> val empty = scala.collection.immutable.Queue[Int]()
161+
empty: scala.collection.immutable.Queue[Int] = Queue()
162+
~~~
163+
{% endtab %}
164+
{% endtabs %}
122165

123166
You can append an element to an immutable queue with `enqueue`:
124167

125-
scala> val has1 = empty.enqueue(1)
126-
has1: scala.collection.immutable.Queue[Int] = Queue(1)
168+
{% tabs Queue_2 %}
169+
{% tab 'Scala 2 and 3' for=Queue_2 %}
170+
~~~scala
171+
scala> val has1 = empty.enqueue(1)
172+
has1: scala.collection.immutable.Queue[Int] = Queue(1)
173+
~~~
174+
{% endtab %}
175+
{% endtabs %}
127176

128177
To append multiple elements to a queue, call `enqueueAll` with a collection as its argument:
129178

130-
scala> val has123 = has1.enqueueAll(List(2, 3))
131-
has123: scala.collection.immutable.Queue[Int]
132-
= Queue(1, 2, 3)
179+
{% tabs Queue_3 %}
180+
{% tab 'Scala 2 and 3' for=Queue_3 %}
181+
~~~scala
182+
scala> val has123 = has1.enqueueAll(List(2, 3))
183+
has123: scala.collection.immutable.Queue[Int]
184+
= Queue(1, 2, 3)
185+
~~~
186+
{% endtab %}
187+
{% endtabs %}
133188

134189
To remove an element from the head of the queue, you use `dequeue`:
135190

136-
scala> val (element, has23) = has123.dequeue
137-
element: Int = 1
138-
has23: scala.collection.immutable.Queue[Int] = Queue(2, 3)
191+
{% tabs Queue_4 %}
192+
{% tab 'Scala 2 and 3' for=Queue_4 %}
193+
~~~scala
194+
scala> val (element, has23) = has123.dequeue
195+
element: Int = 1
196+
has23: scala.collection.immutable.Queue[Int] = Queue(2, 3)
197+
~~~
198+
{% endtab %}
199+
{% endtabs %}
139200

140201
Note that `dequeue` returns a pair consisting of the element removed and the rest of the queue.
141202

142203
## Ranges
143204

144205
A [Range](https://www.scala-lang.org/api/{{ site.scala-version }}/scala/collection/immutable/Range.html) is an ordered sequence of integers that are equally spaced apart. For example, "1, 2, 3," is a range, as is "5, 8, 11, 14." To create a range in Scala, use the predefined methods `to` and `by`.
145206

146-
scala> 1 to 3
147-
res2: scala.collection.immutable.Range.Inclusive = Range(1, 2, 3)
148-
scala> 5 to 14 by 3
149-
res3: scala.collection.immutable.Range = Range(5, 8, 11, 14)
207+
{% tabs Range_1 %}
208+
{% tab 'Scala 2 and 3' for=Range_1 %}
209+
~~~scala
210+
scala> 1 to 3
211+
res2: scala.collection.immutable.Range.Inclusive = Range(1, 2, 3)
212+
scala> 5 to 14 by 3
213+
res3: scala.collection.immutable.Range = Range(5, 8, 11, 14)
214+
~~~
215+
{% endtab %}
216+
{% endtabs %}
150217

151218
If you want to create a range that is exclusive of its upper limit, then use the convenience method `until` instead of `to`:
152219

153-
scala> 1 until 3
154-
res2: scala.collection.immutable.Range = Range(1, 2)
220+
{% tabs Range_2 %}
221+
{% tab 'Scala 2 and 3' for=Range_2 %}
222+
~~~scala
223+
scala> 1 until 3
224+
res2: scala.collection.immutable.Range = Range(1, 2)
225+
~~~
226+
{% endtab %}
227+
{% endtabs %}
155228

156229
Ranges are represented in constant space, because they can be defined by just three numbers: their start, their end, and the stepping value. Because of this representation, most operations on ranges are extremely fast.
157230

@@ -167,11 +240,16 @@ Red-black trees are a form of balanced binary tree where some nodes are designat
167240

168241
Scala provides implementations of immutable sets and maps that use a red-black tree internally. Access them under the names [TreeSet](https://www.scala-lang.org/api/{{ site.scala-version }}/scala/collection/immutable/TreeSet.html) and [TreeMap](https://www.scala-lang.org/api/{{ site.scala-version }}/scala/collection/immutable/TreeMap.html).
169242

170-
171-
scala> scala.collection.immutable.TreeSet.empty[Int]
172-
res11: scala.collection.immutable.TreeSet[Int] = TreeSet()
173-
scala> res11 + 1 + 3 + 3
174-
res12: scala.collection.immutable.TreeSet[Int] = TreeSet(1, 3)
243+
{% tabs Red-Black_1 %}
244+
{% tab 'Scala 2 and 3' for=Red-Black_1 %}
245+
~~~scala
246+
scala> scala.collection.immutable.TreeSet.empty[Int]
247+
res11: scala.collection.immutable.TreeSet[Int] = TreeSet()
248+
scala> res11 + 1 + 3 + 3
249+
res12: scala.collection.immutable.TreeSet[Int] = TreeSet(1, 3)
250+
~~~
251+
{% endtab %}
252+
{% endtabs %}
175253

176254
Red-black trees are the standard implementation of `SortedSet` in Scala, because they provide an efficient iterator that returns all elements in sorted order.
177255

@@ -183,22 +261,30 @@ Internally, bit sets use an array of 64-bit `Long`s. The first `Long` in the arr
183261

184262
Operations on bit sets are very fast. Testing for inclusion takes constant time. Adding an item to the set takes time proportional to the number of `Long`s in the bit set's array, which is typically a small number. Here are some simple examples of the use of a bit set:
185263

186-
scala> val bits = scala.collection.immutable.BitSet.empty
187-
bits: scala.collection.immutable.BitSet = BitSet()
188-
scala> val moreBits = bits + 3 + 4 + 4
189-
moreBits: scala.collection.immutable.BitSet = BitSet(3, 4)
190-
scala> moreBits(3)
191-
res26: Boolean = true
192-
scala> moreBits(0)
193-
res27: Boolean = false
264+
{% tabs BitSet_1 %}
265+
{% tab 'Scala 2 and 3' for=BitSet_1 %}
266+
~~~scala
267+
scala> val bits = scala.collection.immutable.BitSet.empty
268+
bits: scala.collection.immutable.BitSet = BitSet()
269+
scala> val moreBits = bits + 3 + 4 + 4
270+
moreBits: scala.collection.immutable.BitSet = BitSet(3, 4)
271+
scala> moreBits(3)
272+
res26: Boolean = true
273+
scala> moreBits(0)
274+
res27: Boolean = false
275+
~~~
276+
{% endtab %}
277+
{% endtabs %}
194278

195279
## VectorMaps
196280

197281
A [VectorMap](https://www.scala-lang.org/api/{{ site.scala-version }}/scala/collection/immutable/VectorMap.html) represents
198282
a map using both a `Vector` of keys and a `HashMap`. It provides an iterator that returns all the entries in their
199283
insertion order.
200284

201-
~~~
285+
{% tabs VectorMap_1 %}
286+
{% tab 'Scala 2 and 3' for=VectorMap_1 %}
287+
~~~scala
202288
scala> val vm = scala.collection.immutable.VectorMap.empty[Int, String]
203289
vm: scala.collection.immutable.VectorMap[Int,String] =
204290
VectorMap()
@@ -211,6 +297,8 @@ vm2: scala.collection.immutable.VectorMap[Int,String] =
211297
scala> vm2 == Map(2 -> "two", 1 -> "one")
212298
res29: Boolean = true
213299
~~~
300+
{% endtab %}
301+
{% endtabs %}
214302

215303
The first lines show that the content of the `VectorMap` keeps the insertion order, and the last line
216304
shows that `VectorMap`s are comparable with other `Map`s and that this comparison does not take the
@@ -220,8 +308,14 @@ order of elements into account.
220308

221309
A [ListMap](https://www.scala-lang.org/api/{{ site.scala-version }}/scala/collection/immutable/ListMap.html) represents a map as a linked list of key-value pairs. In general, operations on a list map might have to iterate through the entire list. Thus, operations on a list map take time linear in the size of the map. In fact there is little usage for list maps in Scala because standard immutable maps are almost always faster. The only possible exception to this is if the map is for some reason constructed in such a way that the first elements in the list are selected much more often than the other elements.
222310

223-
scala> val map = scala.collection.immutable.ListMap(1->"one", 2->"two")
224-
map: scala.collection.immutable.ListMap[Int,java.lang.String] =
225-
Map(1 -> one, 2 -> two)
226-
scala> map(2)
227-
res30: String = "two"
311+
{% tabs ListMap_1 %}
312+
{% tab 'Scala 2 and 3' for=ListMap_1 %}
313+
~~~scala
314+
scala> val map = scala.collection.immutable.ListMap(1->"one", 2->"two")
315+
map: scala.collection.immutable.ListMap[Int,java.lang.String] =
316+
Map(1 -> one, 2 -> two)
317+
scala> map(2)
318+
res30: String = "two"
319+
~~~
320+
{% endtab %}
321+
{% endtabs %}

0 commit comments

Comments
 (0)