# 9.4: Examples of Key Classes


We present now the most common or important collection classes using simple code examples. The main protocols of collections are: at:, at:put: — to access an element, add:, remove: — to add or remove an element, size, isEmpty, include: — to get some information about the collection, do:, collect:, select: — to iterate over the collection. Each collection may implement or not such protocols, and when they do, they interpret them to fit with their semantics. We suggest you browse the classes themselves to identify specific and more advanced protocols.

We will focus on the most common collection classes: OrderedCollection, Set, SortedCollection, Dictionary, Interval, and Array.

Common creation protocol. There are several ways to create instances of collections. The most generic ones use the methods new: and with:. new: anInteger creates a collection of size anInteger whose elements will all be nil. with: anObject creates a collection and adds anObject to the created collection. Different collections will realize this behaviour differently.

You can create collections with initial elements using the methods with:, with:with: etc. for up to six elements.

Array with: 1    → #(1)
Array with: 1 with: 2    → #(1 2)
Array with: 1 with: 2 with: 3    → #(1 2 3)
Array with: 1 with: 2 with: 3 with: 4    → #(1 2 3 4)
Array with: 1 with: 2 with: 3 with: 4 with: 5    → #(1 2 3 4 5)
Array with: 1 with: 2 with: 3 with: 4 with: 5 with: 6    → #(1 2 3 4 5 6)


You can also use addAll: to add all elements of one kind of collection to another kind:

(1 to: 5) asOrderedCollection addAll: '678'; yourself


## Interval

The class Interval represents ranges of numbers. For example, the interval of numbers from 1 to 100 is defined as follows:

Interval from: 1 to: 100    →    (1 to: 100)


The printString of this interval reveals that the class Number provides us with a convenience method called to: to generate intervals:

(Interval from: 1 to: 100) = (1 to: 100)    →    true


We can use Interval class»from:to:by: or Number»to:by: to specify the step between two numbers as follow:

(Interval from: 1 to: 100 by: 0.5) size    →    199
(1 to: 100 by: 0.5) at: 198    →    99.5
(1/2 to: 54/7 by: 1/3) last    →    (15/2)


## Dictionary

Dictionaries are important collections whose elements are accessed using keys. Among the most commonly used messages of dictionary you will find at:, at:put:, at:ifAbsent:, keys and values.

colors := Dictionary new.
colors at: #yellow put: Color yellow.
colors at: #blue put: Color blue.
colors at: #red put: Color red.
colors at: #yellow    →    Color yellow
colors keys           →    a Set(#blue #yellow #red)
colors values         →    {Color blue . Color yellow . Color red}


Dictionaries compare keys by equality. Two keys are considered to be the same if they return true when compared using =. A common and difficult to spot bug is to use as key an object whose = method has been redefined but not its hash method. Both methods are used in the implementation of dictionary and when comparing objects.

The class Dictionary clearly illustrates that the collection hierarchy is based on subclassing and not subtyping. Even though Dictionary is a subclass of Set, we would normally not want to use a Dictionary where a Set is expected. In its implementation, however, a Dictionary can clearly be seen as consisting of a set of associations (key value) created using the message ->. We can create a Dictionary from a collection of associations, or we may convert a dictionary to an array of associations.

colors := Dictionary newFrom: { #blue->Color blue. #red->Color red.
#yellow->Color yellow }.
colors removeKey: #blue.
colors associations    →    {#yellow->Color yellow . #red->Color red}


IdentityDictionary. While a dictionary uses the result of the messages = and hash to determine if two keys are the same, the class IdentityDictionary uses the identity (message ==) of the key instead of its values, i.e., it considers two keys to be equal only if they are the same object.

Often Symbols are used as keys, in which case it is natural to use an IdentityDictionary, since a Symbol is guaranteed to be globally unique. If, on the other hand, your keys are Strings, it is better to use a plain Dictionary, or you may get into trouble:

a := 'foobar'.
b := a copy.
trouble := IdentityDictionary new.
trouble at: a put: 'a'; at: b put: 'b'.
trouble at: a            →    'a'
trouble at: b            →    'b'
trouble at: 'foobar'     →    'a'


Since a and b are different objects, they are treated as different objects. Interestingly, the literal 'foobar' is allocated just once, so is really the same object as a. You don’t want your code to depend on behaviour like this! A plain Dictionary would give the same value for any key equal to 'foobar'.

Use only globally unique objects (like Symbols or SmallIntegers) as keys for a IdentityDictionary, and Strings (or other objects) as keys for a plain Dictionary.

Note that the global Smalltalk is an instance of SystemDictionary, a subclass of IdentityDictionary, hence all its keys are Symbols (actually, ByteSymbols, which contain only 8-bit characters).

Smalltalk keys collect: [ :each | each class ]    →    a Set(ByteSymbol)


Sending keys or values to a Dictionary results in a Set, which we look at next.

## Set

The class Set is a collection which behaves as a mathematical set, i.e., as a collection with no duplicate elements and without any order. In a Set elements are added using the message add: and they cannot be accessed using the message at:. Objects put in a set should implement the methods hash and =.

s := Set new.
s size    →    2


You can also create sets using Set class»newFrom: or the conversion message Collection»asSet:

(Set newFrom: #(1 2 3 1 4)) = #(1 2 3 4 3 2 1) asSet    →    true


asSet offers us a convenient way to eliminate duplicates from a collection:

{ Color black. Color white. (Color red + Color blue + Color green) } asSet size    →    2


Note that red + blue + green = white.

A Bag is much like a Set except that it does allow duplicates:

{ Color black. Color white. (Color red + Color blue + Color green) } asBag size    →    3


The set operations union, intersection and membership test are implemented by the Collection messages union:, intersection: and includes:. The receiver is first converted to a Set, so these operations work for all kinds of collections!

(1 to: 6) union: (4 to: 10)        →    a Set (1 2 3 4 5 6 7 8 9 10)
'hello' intersection: 'there'      →    'he'
#Smalltalk includes: $k → true  As we explain below, elements of a set are accessed using iterators (see Section 9.5). ## SortedCollection In contrast to an OrderedCollection, a SortedCollection maintains its elements in sort order. By default, a sorted collection uses the message <= to establish sort order, so it can sort instances of subclasses of the abstract class Magnitude, which defines the protocol of comparable objects (<, =, >, >=, between:and:...). (See Chapter 8.) You can create a SortedCollection by creating a new instance and adding elements to it: SortedCollection new add: 5; add: 2; add: 50; add: -10; yourself. → a SortedCollection(-10 2 5 50)  More usually, though, one will send the conversion message asSortedCollection to an existing collection: #(5 2 50 -10) asSortedCollection → a SortedCollection(-10 2 5 50)  This example answers the following FAQ: FAQ: How do you sort a collection? Answer: Send the message asSortedCollection to it. 'hello' asSortedCollection → a SortedCollection($e $h$l $l$o)


How do you get a String back from this result? asString unfortunately returns the printString representation, which is not what we want:

'hello' asSortedCollection asString    →    'a SortedCollection($e$h $l$l $o)'  The correct answer is to either use String class»newFrom:, String class»withAll: or Object»as:: 'hello' asSortedCollection as: String → 'ehllo' String newFrom: ('hello' asSortedCollection) → 'ehllo' String withAll: ('hello' asSortedCollection) → 'ehllo'  It is possible to have different kinds of elements in a SortedCollection as long as they are all comparable. For example we can mix different kinds of numbers such as integers, floats and fractions: { 5. 2/--3. 5.21 } asSortedCollection → a SortedCollection((--2/3) 5 5.21)  Imagine that you want to sort objects that do not define the method <= or that you would like to have a different sorting criterion. You can do this by supplying a two argument block, called a sortblock, to the sorted collection. For example, the class Color is not a Magnitude and it does not implement the method <=, but we can specify a block stating that the colors should be sorted according to their luminance (a measure of brightness). col := SortedCollection sortBlock: [:c1 :c2 | c1 luminance <= c2 luminance]. col addAll: { Color red. Color yellow. Color white. Color black }. col → a SortedCollection(Color black Color red Color yellow Color white)  ## String A Smalltalk String represents a collection of Characters. It is sequenceable, indexable, mutable and homogeneous, containing only Character instances. Like Arrays, Strings have a dedicated syntax, and are normally created by directly specifying a String literal within single quotes, but the usual collection creation methods will work as well. 'Hello' → 'Hello' String with:$A                         →    'A'
String with: $h with:$i with: $! → 'hi!' String newFrom: #($h $e$l $l$o)       →    'hello'


In actual fact, String is abstract. When we instantiate a String we actually get either an 8-bit ByteString or a 32-bit WideString. To keep things simple, we usually ignore the difference and just talk about instances of String.

Two instances of String can be concatenated with a comma.

s := 'no', ' ', 'worries'.
s    →    'no worries'


Since a string is a mutable collection we can also change it using the method at:put:.

s at: 4 put: $h; at: 5 put:$u.
s    →    'nohurries'


Note that the comma method is defined by Collection, so it will work for any kind of collection!

(1 to: 3),'45'    →    #(1 2 3 $4$5)


We can also modify an existing string using replaceAll:with: or replaceFrom:to:with: as shown below. Note that the number of characters and the interval should have the same size.

s replaceAll: $n with:$N.
s    →    'Nohurries'
s replaceFrom: 4 to: 5 with: 'wo'.
s    →    'No worries'


In contrast to the methods described above, the method copyReplaceAll: creates a new string. (Curiously, here the arguments are substrings rather than individual characters, and their sizes do not have to match.)

s copyReplaceAll: 'rries' with: 'mbats'    →    'No wombats'


A quick look at the implementation of these methods reveals that they are defined not only for Strings, but for any kind of SequenceableCollection, so the following also works:

(1 to: 6) copyReplaceAll: (3 to: 5) with: { 'three'. 'etc.' }    →    #(1 2 'three' 'etc.' 6)


String matching. It is possible to ask whether a pattern matches a string by sending the match: message. The pattern can specify * to match an arbitrary series of characters and # to match a single character. Note that match: is sent to the pattern and not the string to be matched.

'Linux *' match: 'Linux mag'                →    true
'GNU/Linux #ag' match: 'GNU/Linux tag'      →    true


Another useful method is findString:.

'GNU/Linux mag' findString: 'Linux'                                        →    5
'GNU/Linux mag' findString: 'linux' startingAt: 1 caseSensitive: false     →    5


More advanced pattern matching facilities offering the capabilities of Perl are also available, but they are not included in the standard image.1

Some tests on strings. The following examples illustrate the use of isEmpty, includes: and anySatisfy: which are further messages defined not only on Strings but more generally on collections.

'Hello' isEmpty    →    false
'Hello' includes: \$a    →    false
'JOE' anySatisfy: [:c | c isLowercase]    →    false
'Joe' anySatisfy: [:c | c isLowercase]    →    true


String templating. There are three messages that are useful to manage string templating: format:, expandMacros and expandMacrosWith:.

'{1} is {2}' format: {'Squeak' . 'cool'}    →    'Squeak is cool'


The messages of the expandMacros family offer variable substitution, using <n> for carriage return, <t> for tabulation, <1s>, <2s>, <3s> for arguments (<1p>, <2p>, surrounds the string with single quotes), and <1?value1:value2> for conditional.

'look-<t>-here' expandMacros                                →    'look- -here'
'<1s> is <2s>' expandMacrosWith: 'Squeak' with: 'cool'      →    'Squeak is cool'
'<2s> is <1s>' expandMacrosWith: 'Squeak' with: 'cool'      →    'cool is Squeak'
'<1p> or <1s>' expandMacrosWith: 'Squeak' with: 'cool'      →    '"Squeak" or Squeak'
'<1?Quentin:Thibaut> plays' expandMacrosWith: true          →    'Quentin plays'
'<1?Quentin:Thibaut> plays' expandMacrosWith: false         →    'Thibaut plays'


Some other utility methods. The class String offers numerous other utilities including the messages asLowercase, asUppercase and capitalized.

'XYZ' asLowercase        →    'xyz'
'xyz' asUppercase        →    'XYZ'
'hilaire' capitalized    →    'Hilaire'
'1.54' asNumber          →    1.54
'this sentence is without a doubt far too long' contractTo: 20    →    'this sent...too long'


Note that there is generally a difference between asking an object its string representation by sending the message printString and converting it to a string by sending the message asString. Here is an example of the difference.

#ASymbol printString    →    '#ASymbol'
#ASymbol asString       →    'ASymbol'


A symbol is similar to a string but is guaranteed to be globally unique. For this reason symbols are preferred to strings as keys for dictionaries, in particular for instances of IdentityDictionary. See also Chapter 8 for more about String and Symbol.

1. We strongly recommend Vassili Bykov’s regular expression package, available at www.squeaksource.com/Regex.html.

This page titled 9.4: Examples of Key Classes is shared under a CC BY-SA 3.0 license and was authored, remixed, and/or curated by Andrew P. Black, Stéphane Ducasse, Oscar Nierstrasz, Damien Pollet via source content that was edited to the style and standards of the LibreTexts platform; a detailed edit history is available upon request.