How to handle hash collisions for Dictionaries in Swift - swift

TLDR
My custom structure implements the Hashable Protocol. However, when hash collisions occur while inserting keys in a Dictionary, they are not automatically handled. How do I overcome this problem?
Background
I had previously asked this question How to implement the Hashable Protocol in Swift for an Int array (a custom string struct). Later I added my own answer, which seemed to be working.
However, recently I have detected a subtle problem with hashValue collisions when using a Dictionary.
Most basic example
I have simplified the code down as far as I can to the following example.
Custom structure
struct MyStructure: Hashable {
var id: Int
init(id: Int) {
self.id = id
}
var hashValue: Int {
get {
// contrived to produce a hashValue collision for id=1 and id=2
if id == 1 {
return 2
}
return id
}
}
}
func ==(lhs: MyStructure, rhs: MyStructure) -> Bool {
return lhs.hashValue == rhs.hashValue
}
Note the global function to overload the equality operator (==) in order to conform to the Equatable Protocol, which is required by the Hashable Protocol.
Subtle Dictionary key problem
If I create a Dictionary with MyStructure as the key
var dictionary = [MyStructure : String]()
let ok = MyStructure(id: 0) // hashValue = 0
let collision1 = MyStructure(id: 1) // hashValue = 2
let collision2 = MyStructure(id: 2) // hashValue = 2
dictionary[ok] = "some text"
dictionary[collision1] = "other text"
dictionary[collision2] = "more text"
print(dictionary) // [MyStructure(id: 2): more text, MyStructure(id: 0): some text]
print(dictionary.count) // 2
the equal hash values cause the collision1 key to be overwritten by the collision2 key. There is no warning. If such a collision only happened once in a dictionary with 100 keys, then it could easily be missed. (It took me quite a while to notice this problem.)
Obvious problem with Dictionary literal
If I repeat this with a dictionary literal, though, the problem becomes much more obvious because a fatal error is thrown.
let ok = MyStructure(id: 0) // hashValue = 0
let collision1 = MyStructure(id: 1) // hashValue = 2
let collision2 = MyStructure(id: 2) // hashValue = 2
let dictionaryLiteral = [
ok : "some text",
collision1 : "other text",
collision2 : "more text"
]
// fatal error: Dictionary literal contains duplicate keys
Question
I was under the impression that it was not necessary for hashValue to always return a unique value. For example, Mattt Thompson says,
One of the most common misconceptions about implementing a custom hash
function comes from ... thinking that hash values must be distinct.
And the respected SO user #Gaffa says that one way to handle hash collisions is to
Consider hash codes to be non-unique, and use an equality comparer for
the actual data to determine uniqueness.
In my opinion, the question Do swift hashable protocol hash functions need to return unique values? has not been adequately answered at the time of this writing.
After reading the Swift Dictionary question How are hash collisions handled?, I assumed that Swift automatically handled hash collisions with Dictionary. But apparently that is not true if I am using a custom class or structure.
This comment makes me think the answer is in how the Equatable protocol is implemented, but I am not sure how I should change it.
func ==(lhs: MyStructure, rhs: MyStructure) -> Bool {
return lhs.hashValue == rhs.hashValue
}
Is this function called for every dictionary key lookup or only when there is a hash collision? (Update: see this question)
What should I do to determine uniqueness when (and only when) a hash collision occurs?

func ==(lhs: MyStructure, rhs: MyStructure) -> Bool {
return lhs.hashValue == rhs.hashValue
}
Note the global function to overload the equality operator (==) in order to conform to the Equatable Protocol, which is required by the Hashable Protocol.
Your problem is an incorrect equality implementation.
A hash table (such as a Swift Dictionary or Set) requires separate equality and hash implementations.
hash gets you close to the object you're looking for; equality gets you the exact object you're looking for.
Your code uses the same implementation for hash and equality, and this will guarantee a collision.
To fix the problem, implement equality to match exact object values (however your model defines equality). E.g.:
func ==(lhs: MyStructure, rhs: MyStructure) -> Bool {
return lhs.id == rhs.id
}

I think you have all the pieces of the puzzle you need -- you just need to put them together. You have a bunch of great sources.
Hash collisions are okay. If a hash collision occurs, objects will be checked for equality instead (only against the objects with matching hashes). For this reason, objects' Equatable conformance needs to be based on something other than hashValue, unless you are certain that hashes cannot collide.
This is the exact reason that objects that conform to Hashable must also conform to Equatable. Swift needs a more domain-specific comparison method for when hashing doesn't cut it.
In that same NSHipster article, you can see how Mattt implements isEqual: versus hash in his example Person class. Specifically, he has an isEqualToPerson: method that checks against other properties of a person (birthdate, full name) to determine equality.
- (BOOL)isEqualToPerson:(Person *)person {
if (!person) {
return NO;
}
BOOL haveEqualNames = (!self.name && !person.name) || [self.name isEqualToString:person.name];
BOOL haveEqualBirthdays = (!self.birthday && !person.birthday) || [self.birthday isEqualToDate:person.birthday];
return haveEqualNames && haveEqualBirthdays;
}
He does not use a hash value when checking for equality - he uses properties specific to his person class.
Likewise, Swift does not let you simply use a Hashable object as a dictionary key -- implicitly, by protocol inheritance -- keys must conform to Equatable as well. For standard library Swift types this has already been taken care of, but for your custom types and class, you must create your own == implementation. This is why Swift does not automatically handle dictionary collisions with custom types - you must implement Equatable yourself!
As a parting thought, Mattt also states that you can often just do an identity check to make sure your two objects are at different memory address, and thus different objects. In Swift, that would like like this:
if person1 === person2 {
// ...
}
There is no guarantee here that person1 and person2 have different properties, just that they occupy separate space in memory. Conversely, in the earlier isEqualToPerson: method, there is no guarantee that two people with the same names and birthdates are actually same people. Thus, you have to consider what makes sense for you specific object type. Again, another reason that Swift does not implement Equatable for you on custom types.

the equal hash values cause the collision1 key to be overwritten by
the collision2 key. There is no warning. If such a collision only
happened once in a dictionary with 100 keys, then it could easily be
missed.
Hash collision has nothing to do with it. (Hash collisions never affect the result, only the performance.) It is working exactly as documented.
Dictionary operations work on equality (==) of keys. Dictionaries do not contain duplicate keys (meaning keys that are equal). When you set a value with a key, it overwrites any entry containing an equal key. When you get an entry with a subscript, it finds a value with a key that is equal to, not necessarily the same as, the thing you gave. And so on.
collision1 and collision2 are equal (==), based on the way you defined the == operator. Therefore, setting an entry with key collision2 must overwrite any entry with key collision1.
P.S. The same exact thing applies with dictionaries in other languages. For example, in Cocoa, NSDictionary does not allow duplicate keys, i.e. keys that are isEqual:. In Java, Maps do not allow duplicate keys, i.e. keys that are .equals().

You can see my comments on this page's answers and this answer. I think all answers are still written in a VERY confusing way.
tl;dr 0) you don't need to write the implementation isEqual ie == between the hashValues. 1) Only provide/return hashValue. 2) just implement Equatable as you normally would
0) To conform to hashable you must have a computed value named hashValue and give it an appropriate value. Unlike equatable protocol, the comparison of hashValues is already there. You DON'T need to write:
func ==(lhs: MyStructure, rhs: MyStructure) -> Bool {
return lhs.hashValue == rhs.hashValue
// Snippet A
}
1) Then it uses the hashValue to check to see if for that hashValue's index (calculated by its modulo against the array's count) the key being looked exists or not. It looks within the array of key/value pairs of that index.
2) However as a fail safe ie in case there are matching hashes you fall back to the regular == func. (Logically you need it because of a fail safe. But you also you need it because Hashable protocol conforms to Equatable and therefore you must write an implementation for ==. Otherwise you would get a compiler error)
func == (lhs: MyStructure, rhs: MyStructure) -> Bool {
return lhs.id == rhs.id
//Snippet B
}
Conclusion:
You must include Snippet B, exclude Snippet A, while also having a hashValue property

Related

What does Hash Values for Set Types in swift?

when i read swift documentation i can not understand what that mean?.
A type must be hashable in order to be stored in a set—that is, the type must provide a way to compute a hash value for itself. A hash value is an Int value that is the same for all objects that compare equally, such that if a == b, it follows that a.hashValue == b.hashValue.
Sets are designed for performance. The contains(_:) method on Set is O(1) complexity meaning it takes a constant amount of time to perform no matter the size of the set. contains(_:) on an Array is O(n) meaning the time to determine if the array contains an element increases as the size of the array increases.
How does a Set do that? It uses a hashValue for the items of the Set and contains an internal dictionary like structure that maps the hashValue to the list of items with that hashValue. This makes testing equality of complex structures (like String) very quick because Set first tests if two values have the same hashValue. If the hashValues are different, it doesn't need to check the contents of the structs themselves.
To test if the Set contains a value, it is necessary to only look up the hashValue in the dictionary and then compare the item to the values that match.
So, for items to be contained in a Set, it is important to provide a hashing function that:
Is the same if two structs/objects are equal. (absolute requirement)
Computes a wide range of hashValues so that the items are widely distributed and don't require falling back to the slower check for equality. (for good performance)
Here is an example of a Hashable struct that is appropriate for storage in a Set:
struct Option: Hashable, CustomStringConvertible {
var title: String
var key: String
var value: Int
var description: String { return "{title: \"\(title)\", key: \"\(key)\", value: \(value)}" }
func hash(into hasher: inout Hasher) {
hasher.combine(title)
hasher.combine(key)
}
static func ==(lhs: Option, rhs: Option) -> Bool {
return lhs.title == rhs.title && lhs.key == rhs.key
}
}
Note: In this example, only the title and key properties are considered for equality. Two structs can be equal even if they have different value properties. The hash(into:) function likewise only considers the title and key properties.

Could not cast value of type 'Swift.Array<Swift.String>' to 'Swift.AnyHashable' [duplicate]

I am making a structure that acts like a String, except that it only deals with Unicode UTF-32 scalar values. Thus, it is an array of UInt32. (See this question for more background.)
What I want to do
I want to be able to use my custom ScalarString struct as a key in a dictionary. For example:
var suffixDictionary = [ScalarString: ScalarString]() // Unicode key, rendered glyph value
// populate dictionary
suffixDictionary[keyScalarString] = valueScalarString
// ...
// check if dictionary contains Unicode scalar string key
if let renderedSuffix = suffixDictionary[unicodeScalarString] {
// do something with value
}
Problem
In order to do that, ScalarString needs to implement the Hashable Protocol. I thought I would be able to do something like this:
struct ScalarString: Hashable {
private var scalarArray: [UInt32] = []
var hashValue : Int {
get {
return self.scalarArray.hashValue // error
}
}
}
func ==(left: ScalarString, right: ScalarString) -> Bool {
return left.hashValue == right.hashValue
}
but then I discovered that Swift arrays don't have a hashValue.
What I read
The article Strategies for Implementing the Hashable Protocol in Swift had a lot of great ideas, but I didn't see any that seemed like they would work well in this case. Specifically,
Object property (array is does not have hashValue)
ID property (not sure how this could be implemented well)
Formula (seems like any formula for a string of 32 bit integers would be processor heavy and have lots of integer overflow)
ObjectIdentifier (I'm using a struct, not a class)
Inheriting from NSObject (I'm using a struct, not a class)
Here are some other things I read:
Implementing Swift's Hashable Protocol
Swift Comparison Protocols
Perfect hash function
Membership of custom objects in Swift Arrays and Dictionaries
How to implement Hashable for your custom class
Writing a good Hashable implementation in Swift
Question
Swift Strings have a hashValue property, so I know it is possible to do.
How would I create a hashValue for my custom structure?
Updates
Update 1: I would like to do something that does not involve converting to String and then using String's hashValue. My whole point for making my own structure was so that I could avoid doing lots of String conversions. String gets it's hashValue from somewhere. It seems like I could get it using the same method.
Update 2: I've been looking into the implementation of string hash codes algorithms from other contexts. I'm having a little difficulty knowing which is best and expressing them in Swift, though.
Java hashCode algorithm
C algorithms
hash function for string (SO question and answers in C)
Hashing tutorial (Virginia Tech Algorithm Visualization Research Group)
General Purpose Hash Function Algorithms
Update 3
I would prefer not to import any external frameworks unless that is the recommended way to go for these things.
I submitted a possible solution using the DJB Hash Function.
Update
Martin R writes:
As of Swift 4.1, the compiler can synthesize Equatable and Hashable
for types conformance automatically, if all members conform to
Equatable/Hashable (SE0185). And as of Swift 4.2, a high-quality hash
combiner is built-in into the Swift standard library (SE-0206).
Therefore there is no need anymore to define your own hashing
function, it suffices to declare the conformance:
struct ScalarString: Hashable, ... {
private var scalarArray: [UInt32] = []
// ... }
Thus, the answer below needs to be rewritten (yet again). Until that happens refer to Martin R's answer from the link above.
Old Answer:
This answer has been completely rewritten after submitting my original answer to code review.
How to implement to Hashable protocol
The Hashable protocol allows you to use your custom class or struct as a dictionary key. In order to implement this protocol you need to
Implement the Equatable protocol (Hashable inherits from Equatable)
Return a computed hashValue
These points follow from the axiom given in the documentation:
x == y implies x.hashValue == y.hashValue
where x and y are values of some Type.
Implement the Equatable protocol
In order to implement the Equatable protocol, you define how your type uses the == (equivalence) operator. In your example, equivalence can be determined like this:
func ==(left: ScalarString, right: ScalarString) -> Bool {
return left.scalarArray == right.scalarArray
}
The == function is global so it goes outside of your class or struct.
Return a computed hashValue
Your custom class or struct must also have a computed hashValue variable. A good hash algorithm will provide a wide range of hash values. However, it should be noted that you do not need to guarantee that the hash values are all unique. When two different values have identical hash values, this is called a hash collision. It requires some extra work when there is a collision (which is why a good distribution is desirable), but some collisions are to be expected. As I understand it, the == function does that extra work. (Update: It looks like == may do all the work.)
There are a number of ways to calculate the hash value. For example, you could do something as simple as returning the number of elements in the array.
var hashValue: Int {
return self.scalarArray.count
}
This would give a hash collision every time two arrays had the same number of elements but different values. NSArray apparently uses this approach.
DJB Hash Function
A common hash function that works with strings is the DJB hash function. This is the one I will be using, but check out some others here.
A Swift implementation provided by #MartinR follows:
var hashValue: Int {
return self.scalarArray.reduce(5381) {
($0 << 5) &+ $0 &+ Int($1)
}
}
This is an improved version of my original implementation, but let me also include the older expanded form, which may be more readable for people not familiar with reduce. This is equivalent, I believe:
var hashValue: Int {
// DJB Hash Function
var hash = 5381
for(var i = 0; i < self.scalarArray.count; i++)
{
hash = ((hash << 5) &+ hash) &+ Int(self.scalarArray[i])
}
return hash
}
The &+ operator allows Int to overflow and start over again for long strings.
Big Picture
We have looked at the pieces, but let me now show the whole example code as it relates to the Hashable protocol. ScalarString is the custom type from the question. This will be different for different people, of course.
// Include the Hashable keyword after the class/struct name
struct ScalarString: Hashable {
private var scalarArray: [UInt32] = []
// required var for the Hashable protocol
var hashValue: Int {
// DJB hash function
return self.scalarArray.reduce(5381) {
($0 << 5) &+ $0 &+ Int($1)
}
}
}
// required function for the Equatable protocol, which Hashable inheirits from
func ==(left: ScalarString, right: ScalarString) -> Bool {
return left.scalarArray == right.scalarArray
}
Other helpful reading
Which hashing algorithm is best for uniqueness and speed?
Overflow Operators
Why are 5381 and 33 so important in the djb2 algorithm?
How are hash collisions handled?
Credits
A big thanks to Martin R over in Code Review. My rewrite is largely based on his answer. If you found this helpful, then please give him an upvote.
Update
Swift is open source now so it is possible to see how hashValue is implemented for String from the source code. It appears to be more complex than the answer I have given here, and I have not taken the time to analyze it fully. Feel free to do so yourself.
Edit (31 May '17): Please refer to the accepted answer. This answer is pretty much just a demonstration on how to use the CommonCrypto Framework
Okay, I got ahead and extended all arrays with the Hashable protocol by using the SHA-256 hashing algorithm from the CommonCrypto framework. You have to put
#import <CommonCrypto/CommonDigest.h>
into your bridging header for this to work. It's a shame that pointers have to be used though:
extension Array : Hashable, Equatable {
public var hashValue : Int {
var hash = [Int](count: Int(CC_SHA256_DIGEST_LENGTH) / sizeof(Int), repeatedValue: 0)
withUnsafeBufferPointer { ptr in
hash.withUnsafeMutableBufferPointer { (inout hPtr: UnsafeMutableBufferPointer<Int>) -> Void in
CC_SHA256(UnsafePointer<Void>(ptr.baseAddress), CC_LONG(count * sizeof(Element)), UnsafeMutablePointer<UInt8>(hPtr.baseAddress))
}
}
return hash[0]
}
}
Edit (31 May '17): Don't do this, even though SHA256 has pretty much no hash collisions, it's the wrong idea to define equality by hash equality
public func ==<T>(lhs: [T], rhs: [T]) -> Bool {
return lhs.hashValue == rhs.hashValue
}
This is as good as it gets with CommonCrypto. It's ugly, but fast and not manypretty much no hash collisions for sure
Edit (15 July '15): I just made some speed tests:
Randomly filled Int arrays of size n took on average over 1000 runs
n -> time
1000 -> 0.000037 s
10000 -> 0.000379 s
100000 -> 0.003402 s
Whereas with the string hashing method:
n -> time
1000 -> 0.001359 s
10000 -> 0.011036 s
100000 -> 0.122177 s
So the SHA-256 way is about 33 times faster than the string way. I'm not saying that using a string is a very good solution, but it's the only one we can compare it to right now
It is not a very elegant solution but it works nicely:
"\(scalarArray)".hashValue
or
scalarArray.description.hashValue
Which just uses the textual representation as a hash source
One suggestion - since you are modeling a String, would it work to convert your [UInt32] array to a String and use the String's hashValue? Like this:
var hashValue : Int {
get {
return String(self.scalarArray.map { UnicodeScalar($0) }).hashValue
}
}
That could conveniently allow you to compare your custom struct against Strings as well, though whether or not that is a good idea depends on what you are trying to do...
Note also that, using this approach, instances of ScalarString would have the same hashValue if their String representations were canonically equivalent, which may or may not be what you desire.
So I suppose that if you want the hashValue to represent a unique String, my approach would be good. If you want the hashValue to represent a unique sequence of UInt32 values, #Kametrixom's answer is the way to go...

When Set.insert() returns oldMember that is distinguishable from newMember?

The documentation for Set's insert method states:
Return Value
(true, newMember) if newMember was not contained in the set. If an element equal to newMember was already contained in the set, the method returns (false, oldMember), where oldMember is the element that was equal to newMember.
In some cases, oldMember may be distinguishable from newMember by identity comparison or some other means.
What are the cases when oldMember might be distinguishable from newMember they are talking about? Any example?
Reason for the question: I have a Set that contains objects that are not necessarily Hashable. I had the idea to use Unmanaged.passUnretained(object).toOpaque().hashValue to get a hash-value for just any object (basically from its address) in a wrapper struct, however the above bit in the documentation makes me wary.
If you have some way to tell two instances apart even when they compare equal by their implementation of Equatable's == operator, then you can use this to compare the element to insert with the memberAfterInsert returned by insert(_:).
A prime example of this, as the documentation says, is the identity comparison (===). Two object instances that compare equal may not necessarily be the exact same instance (i.e have the same address).
For example:
class Foo : Hashable {
static func ==(lhs: Foo, rhs: Foo) -> Bool {
return lhs.foo == rhs.foo
}
var hashValue: Int {
return foo
}
let foo: Int
init(_ foo: Int) {
self.foo = foo
}
}
var set : Set = [Foo(10)]
let toInsert = Foo(10)
let result = set.insert(toInsert) // the element toInsert will compare equal to the element
// already in the set, thus the element already in the
// already in the set will be returned.
print(result) // (false, Foo) – false, as element was already in set
print(result.memberAfterInsert == toInsert) // true – obviously they were equal.
print(result.memberAfterInsert === toInsert) // false – but they weren't the same instance.
In your case of using a hash value based on the identity, as long as your Equatable implementation also relies on the identity, you won't break the contract of Hashable (instances that compare equal with == must have the same hashValue) – although obviously you won't be able to use the identity to differentiate the memberAfterInsert with the element you are trying to insert.
However it's worth noting that having a Set where the Element's equality implementation is solely based on identity may cause surprising results for objects that look like they should be equivalent. For example, it would be perfectly fine to have a set of [Foo(5), Foo(5), Foo(5), Foo(5)], given that the objects are different instances.

What is the use of Hashable and Equatable in Swift? When to use which?

I know that Hashable is inherited from Equatable, but can you give me an example that requires Hashable, not just Equatable. Thanks!
You could use hashValue to determine if two objects are not equal to each other.
Apart from that, you shouldn't make any Equatable or Comparable determination of an object solely based on the object's hash value.
In short, Hashable is derived from Equatable because you can't safely determine if two objects are equal to each other merely by testing their hashValue for equality.
Further details
Per the Swift documentation:
A hash value is an Int value that is the same for all objects that compare equally, such that if a == b, it follows that a.hashValue == b.hashValue.
However, the inverse is not true. If a.hashValue == b.hashValue, it does not follow that a == b.
While there are perfect hash functions that return unique values, a hash function could return the same hashValue for two or more different objects. This is known as a hash collision.
Avoid trying to do this
Sweeper's answer says:
For Hashable, because you can get a number that represents the object, you can kind of treat the objects as numbers. You can compare the objects: whether it is less than, greater than, or equal to another object, just like you do with numbers:
No. What you're comparing are the hash values of the (possibly collided) objects, not the objects themselves.
Comparing whether a hash value is less than or greater than another hash value is meaningless with respect to the object.
In regard to equality, it would only hold true if a hash function returned a perfectly unique value for the object.
Hashable itself has very limited use, apart from Equatable. For safety, you should always test the objects themselves.
When you conform to Hashable, you provide a method that returns the hash value of self.
When you conform to Equatable, you provide a method that returns whether the given object and self are equal.
They seem to serve two very different purposes, why does Hashable inherit Equatable? Because the hash values for two equal objects are equal!
What can and can't you do with Hashable and Equatable?
Equatable has more limited uses than Hashable. It can only compare the equality of two objects, and that's it.
For Hashable, because you can get a number that represents the object, you can kind of treat the objects as numbers. You can compare the objects: whether it is less than, greater than, or equal to another object, just like you do with numbers:
if objA.hashValue > objB.hashValue
This also means you can sort objects with Hashable.
Last but not least, you can use Hashable objects as keys for maps! this is because maps' keys cannot duplicate, so how can the system check whether you put a duplicate item in it? It uses the hash values of the keys!
To understand the difference between Hashable and Equitable, consider NoOneModel is a struct and it have two integers x and y
struct NoOneModel {
var x = 0
var y = 0
}
let's create a two instances model1 and model2
let model1 = NoOneModel(x: 1, y: 1)
let model2 = NoOneModel(x: 1, y: 2) /// y value is different
print(model1 == model2) /// false
if we check model1 and model2 are equal? then it will return false, because y value is different in both models
let's confirm a Hashable protocol and combine x value, it indicates that hashValue will create only from x
extension NoOneModel: Hashable {
func hash(into hasher: inout Hasher) {
hasher.combine(x)
}
}
now check it's HashValue returns same for both models because we combine only x value and x value is 1 in both models
print(model1.hashValue == model2.hashValue) /// true
let's confirm Equitable protocol and implement == function.
extension NoOneModel: Equatable {
static func == (lhs: NoOneModel, rhs: NoOneModel) -> Bool {
return lhs.x == rhs.x
}
}
now try to compare both models
print(model1 == model2) /// true
Here the y value of both models are different even if check for == condition it will return true because equitable protocol is included for only x value.
Note: Hashable is inherited from Equitable, it indicates that we can
add == directly without reconfirming the Equitable protocol
Hope this will helps to understand the difference between hashable and equitable protocols.
Thank you.

Why does not Dictionary adopt MutableCollectionType protocol?

While implementing a custom collection type (and therefore making it to adhere to CollectionType protocol) I came to wonder why MutableCollectionType is not adopted by Dictionary type?
From the documentation for MutableCollectionType:
A collection that supports subscript assignment.
For any instance a of a type conforming to MutableCollectionType, :
a[i] = x
let y = a[i]
is equivalent to:
a[i] = x
let y = x
Therefore, it would seem "logical" that Dictionary also adopts this protocol. However, after checking out header files as well as docs, it seems that only Array and related types do that.
What's so special about MutableCollectionType, or about Dictionary, or both for that matter? Should my dictionary-like custom collection type also avoid adopting MutableCollectionType for some reason?
A glance through the protocol reference describes it as having methods like sort and partition. It also has an internal type call SubSequence. These are meaningless with dictionaries. There are no order within a dictionary.
From the headers:
Whereas an arbitrary sequence may be consumed as it is traversed, a collection is multi-pass: any element may be revisited merely by saving its index.
That makes no sense for a dictionary, as a dictionary is unordered. Just because the entry keyed by "howdy" is at index 2 right now does not mean it will be at index 2 one minute from now. In particular, it makes no sense to say "insert this key at index 2" - it is the keys and the internal hashing that provide the order. The indexes have no persistent life of their own. Thus, it is a collection (it has indexes), but not a mutable collection (you can't write into it by index).
To understand the declaration of MutableCollectionType protocol, you first need to know a concept called subscript.
When you write “let y = dic[key]”, Swift is calling a method called subscript getter:
subscript (key: Key) -> Value? { get }
And when you write “dic[key] = x”, Swift is calling a method called subscript setter:
subscript (key: Key) -> Value? { set }
Now let's look at the MutableCollectionType protocol. Dictionary does not conform to MutableCollectionType. because the required methods of this protocol is not implemented in Dictionary.
One of the required method is
public subscript (position: Self.Index) -> Self.Generator.Element { get set }
This subscript method is not the same as the above two we use every day. The type of position is Self.Index, which is DictionaryIndex<Key, Value> for Dictionary type. And the return type Self.Generator.Element is (Key, Value). I think This index type DictionaryIndex is something related to the hash table implementation, which can be used to directly refer to an hash table element. When you use the setter of the subscript you will write something like
dic[index] = (key, value)
It certainly makes no sense to replace a hash map element with another key value pair. This subscript setter is never implemented by Dictionary, so it does not conform to MutableCollectionType protocol.