Dictionary.st
changeset 1053 ac1ea83233e6
parent 641 ef3bcf9de54b
child 1056 991ed2a9318b
--- a/Dictionary.st	Fri Mar 01 21:25:19 1996 +0100
+++ b/Dictionary.st	Fri Mar 01 21:25:46 1996 +0100
@@ -11,10 +11,10 @@
 "
 
 Set subclass:#Dictionary
-	 instanceVariableNames:'valueArray'
-	 classVariableNames:''
-	 poolDictionaries:''
-	 category:'Collections-Unordered'
+	instanceVariableNames:'valueArray'
+	classVariableNames:''
+	poolDictionaries:''
+	category:'Collections-Unordered'
 !
 
 !Dictionary class methodsFor:'documentation'!
@@ -192,26 +192,31 @@
 !
 
 at:aKey put:anObject
-    "add the argument anObject under key, aKey to the receiver"
+    "add the argument anObject under key, aKey to the receiver.
+
+     WARNING: do not add elements while iterating over the receiver.
+              Iterate over a copy to do this."
 
     |index|
 
     aKey isNil ifTrue:[
-	"nil is not allowed as key"
-	self errorInvalidKey:aKey
+        "nil is not allowed as key"
+        self errorInvalidKey:aKey
     ] ifFalse:[
-	index := self findKeyOrNil:aKey.
-	(valueArray basicAt:index) notNil ifTrue:[
-	    valueArray basicAt:index put:anObject.
-	    ^ anObject
-	].
-	keyArray basicAt:index put:aKey.
-	valueArray basicAt:index put:anObject.
-	tally := tally + 1.
+        index := self findKeyOrNil:aKey.
+        (valueArray basicAt:index) notNil ifTrue:[
+            valueArray basicAt:index put:anObject.
+            ^ anObject
+        ].
+        keyArray basicAt:index put:aKey.
+        valueArray basicAt:index put:anObject.
+        tally := tally + 1.
 
-	self fullCheck.
+        self fullCheck.
     ].
     ^ anObject
+
+    "Modified: 1.3.1996 / 21:24:13 / cg"
 !
 
 keyAtEqualValue:aValue
@@ -316,68 +321,93 @@
 !Dictionary methodsFor:'adding & removing'!
 
 add:anAssociation
-    "add the argument, anAssociation to the receiver"
+    "add the argument, anAssociation to the receiver.
+
+     WARNING: do not add elements while iterating over the receiver.
+              Iterate over a copy to do this."
 
     self at:(anAssociation key) put:(anAssociation value).
     ^ anAssociation
+
+    "Modified: 1.3.1996 / 21:23:53 / cg"
 !
 
 declare:key from:aDictionary
     "if the receiver does not include an association for key,
      take the association from aDictionary and add it to the receiver.
      If aDictionary does not contain such an association, use nil
-     as the value of the new dictionary."
+     as the value of the new dictionary.
+
+     WARNING: do not add elements while iterating over the receiver.
+              Iterate over a copy to do this."
 
     |value|
 
     (self includesKey:key) ifFalse:[
-	value := aDictionary at:key ifAbsent:nil.
-	self at:key put:value.
+        value := aDictionary at:key ifAbsent:nil.
+        self at:key put:value.
     ]
+
+    "Modified: 1.3.1996 / 21:24:03 / cg"
 !
 
 remove:oldObject ifAbsent:aBlock
     "remove oldObject from the collection and return it.
      If it was not in the collection return the value of aBlock.
-     This is blocked here; you have to use #removeKey:,
-     #removeAssociation: or #removeValue:"
+
+     This is blocked here; you have to use one of
+     #removeKey:, #saveRemoveKey:, #removeAssociation:,
+     #removeValue: or #saveRemoveValue:"
 
     ^ self shouldNotImplement
+
+    "Modified: 1.3.1996 / 21:21:38 / cg"
 !
 
 removeAssociation:assoc
     "remove the association from the collection.
      If it was not in the collection report an error.
      Only the key is used in the passed argument, and a new
-     association, for the key and the previously stored value is returned."
+     association, for the key and the previously stored value is returned.
+
+     WARNING: do not remove elements while iterating over the receiver.
+              See #saveRemoveKey: to do this."
 
     |key|
 
     key := assoc key.
     ^ key -> (self removeKey:key)
 
-    "Modified: 24.11.1995 / 13:04:09 / cg"
+    "Modified: 1.3.1996 / 21:21:11 / cg"
 !
 
 removeKey:aKey
     "remove the association under aKey from the collection.
-     If it was not in the collection report an error"
+     If it was not in the collection report an error.
+
+     WARNING: do not remove elements while iterating over the receiver.
+              See #saveRemoveKey: to do this."
 
     ^ self removeKey:aKey ifAbsent:[self errorKeyNotFound:aKey]
+
+    "Modified: 1.3.1996 / 21:21:52 / cg"
 !
 
 removeKey:aKey ifAbsent:aBlock
     "remove the association under aKey from the collection,
      return the value previously stored there..
      If it was not in the collection return the result 
-     from evaluating aBlock"
+     from evaluating aBlock.
+
+     WARNING: do not remove elements while iterating over the receiver.
+             See #saveRemoveKey: to do this."
 
     |index "{ Class:SmallInteger }"
      next  "{ Class:SmallInteger }" 
      oldValue|
 
     aKey isNil ifTrue:[
-	^ self errorInvalidKey:aKey
+        ^ self errorInvalidKey:aKey
     ].
 
     "/   below, I could have written:
@@ -395,21 +425,21 @@
 
     tally := tally - 1.
     tally == 0 ifTrue:[
-	self setTally:0
+        self setTally:0
     ] ifFalse:[
-	index == keyArray basicSize ifTrue:[
-	    next := 1
-	] ifFalse:[
-	    next := index + 1.
-	].
-	(keyArray basicAt:next) notNil ifTrue:[
-	    keyArray basicAt:index put:DeletedEntry
-	].
-	self emptyCheck
+        index == keyArray basicSize ifTrue:[
+            next := 1
+        ] ifFalse:[
+            next := index + 1.
+        ].
+        (keyArray basicAt:next) notNil ifTrue:[
+            keyArray basicAt:index put:DeletedEntry
+        ].
+        self emptyCheck
     ].
     ^ oldValue
 
-    "Modified: 24.11.1995 / 13:00:51 / cg"
+    "Modified: 1.3.1996 / 21:21:01 / cg"
 !
 
 removeValue:aValue ifAbsent:aBlock
@@ -420,44 +450,232 @@
      but identity compare in the IdentityDictionary subclass.
 
      Notice, this does a linear search through the values and may
-     therefore be slow for big dictionaries."
+     therefore be slow for big dictionaries.
+
+     WARNING: do not remove elements while iterating over the receiver.
+             See #saveRemoveValue: to do this."
 
     |next  "{ Class:SmallInteger }" 
      oldKey|
 
     aValue notNil ifTrue:[
-	keyArray keysAndValuesDo:[:index :aKey |
-	    |idx "{Class:SmallInteger}"|
+        keyArray keysAndValuesDo:[:index :aKey |
+            |idx "{Class:SmallInteger}"|
 
-	    (aKey notNil and:[aKey ~~ DeletedEntry]) ifTrue:[
-		idx := index.
-		(self compareSame:(valueArray at:idx) with:aValue) ifTrue:[  
-		    "found it"
-		    valueArray basicAt:idx put:nil.
-		    oldKey := keyArray basicAt:idx.
-		    keyArray basicAt:idx put:nil.
-		    tally := tally - 1.
-		    tally == 0 ifTrue:[
-			self setTally:0.
-			^ oldKey
-		    ].
-		    idx == keyArray basicSize ifTrue:[
-			next := 1
-		    ] ifFalse:[
-			next := index + 1.
-		    ].
-		    (keyArray basicAt:next) notNil ifTrue:[
-			keyArray basicAt:idx put:DeletedEntry
-		    ].
-		    self emptyCheck.
-		    ^ oldKey
-		]
-	    ]
-	]
+            (aKey notNil and:[aKey ~~ DeletedEntry]) ifTrue:[
+                idx := index.
+                (self compareSame:(valueArray at:idx) with:aValue) ifTrue:[  
+                    "found it"
+                    valueArray basicAt:idx put:nil.
+                    oldKey := keyArray basicAt:idx.
+                    keyArray basicAt:idx put:nil.
+                    tally := tally - 1.
+                    tally == 0 ifTrue:[
+                        self setTally:0.
+                        ^ oldKey
+                    ].
+                    idx == keyArray basicSize ifTrue:[
+                        next := 1
+                    ] ifFalse:[
+                        next := index + 1.
+                    ].
+                    (keyArray basicAt:next) notNil ifTrue:[
+                        keyArray basicAt:idx put:DeletedEntry
+                    ].
+                    self emptyCheck.
+                    ^ oldKey
+                ]
+            ]
+        ]
     ].
     ^ aBlock value
 
-    "Modified: 24.11.1995 / 13:11:25 / cg"
+    "Modified: 1.3.1996 / 21:22:11 / cg"
+!
+
+saveRemoveKey:aKey
+    "remove the association under aKey from the collection.
+     Return the value previously stored there.
+     If it was not in the collection return nil.
+
+     In contrast to #removeKey:, this does not resize the underlying collection
+     and therefore does NOT rehash & change the elements order.
+     Therefor this can be used while enumerating the receiver,
+     which is not possible if #removeKey: is used.
+
+     WARNING: since no resizing is done, the physical amount of memory used
+              by the container remains the same, although the logical size shrinks.
+              You may want to manually resize the receiver using #emptyCheck."
+
+    |index "{ Class:SmallInteger }"
+     next  "{ Class:SmallInteger }" 
+     oldValue|
+
+    aKey isNil ifTrue:[^ nil].
+
+    index := self find:aKey ifAbsent:0.
+    index == 0 ifTrue:[^ nil].
+
+    oldValue := valueArray basicAt:index.
+
+    valueArray basicAt:index put:nil.
+    keyArray basicAt:index put:nil.
+
+    tally := tally - 1.
+    tally ~~ 0 ifTrue:[
+        index == keyArray basicSize ifTrue:[
+            next := 1
+        ] ifFalse:[
+            next := index + 1.
+        ].
+        (keyArray basicAt:next) notNil ifTrue:[
+            keyArray basicAt:index put:DeletedEntry
+        ].
+    ].
+    ^ oldValue
+
+    "does NOT work:
+
+        |d|
+
+        d := Dictionary new.
+        d at:'one' put:1.
+        d at:'two' put:2.
+        d at:'three' put:3.
+        d at:'four' put:4.
+        d at:'five' put:5.
+        d at:'six' put:6.
+        d at:'seven' put:7.
+        d at:'eight' put:8.
+        d at:'nine' put:9.
+        d keysAndValuesDo:[:k :v |
+            v odd ifTrue:[
+                d removeKey:k
+            ]
+        ].
+        d inspect
+    "
+
+    "DOES work:
+
+        |d|
+
+        d := Dictionary new.
+        d at:'one' put:1.
+        d at:'two' put:2.
+        d at:'three' put:3.
+        d at:'four' put:4.
+        d at:'five' put:5.
+        d at:'six' put:6.
+        d at:'seven' put:7.
+        d at:'eight' put:8.
+        d at:'nine' put:9.
+        d keysAndValuesDo:[:k :v |
+            v odd ifTrue:[
+                d saveRemoveKey:k
+            ]
+        ].
+        d inspect
+    "
+
+    "Created: 1.3.1996 / 21:14:42 / cg"
+    "Modified: 1.3.1996 / 21:14:53 / cg"
+!
+
+saveRemoveValue:aValue
+    "remove the (first) association to aValue from the collection,
+     return the key under which it was stored previously.
+     If it was not in the collection return nil.
+     The value is searched using equality compare here,
+     but identity compare in the IdentityDictionary subclass.
+
+     In contrast to #removeValue:, this does not resize the underlying collection
+     and therefore does NOT rehash & change the elements order.
+     Therefor this can be used while enumerating the receiver,
+     which is not possible if #removeValue: is used.
+
+     WARNING: since no resizing is done, the physical amount of memory used
+              by the container remains the same, although the logical size shrinks.
+              You may want to manually resize the receiver using #emptyCheck."
+
+    |next  "{ Class:SmallInteger }" 
+     oldKey|
+
+    aValue notNil ifTrue:[
+        keyArray keysAndValuesDo:[:index :aKey |
+            |idx "{Class:SmallInteger}"|
+
+            (aKey notNil and:[aKey ~~ DeletedEntry]) ifTrue:[
+                idx := index.
+                (self compareSame:(valueArray at:idx) with:aValue) ifTrue:[  
+                    "found it"
+                    valueArray basicAt:idx put:nil.
+                    oldKey := keyArray basicAt:idx.
+                    keyArray basicAt:idx put:nil.
+                    tally := tally - 1.
+                    tally ~~ 0 ifTrue:[
+                        idx == keyArray basicSize ifTrue:[
+                            next := 1
+                        ] ifFalse:[
+                            next := index + 1.
+                        ].
+                        (keyArray basicAt:next) notNil ifTrue:[
+                            keyArray basicAt:idx put:DeletedEntry
+                        ].
+                    ].
+                    ^ oldKey
+                ]
+            ]
+        ]
+    ].
+    ^ aValue
+
+    "does NOT work:
+
+        |d|
+
+        d := Dictionary new.
+        d at:'one' put:1.
+        d at:'two' put:2.
+        d at:'three' put:3.
+        d at:'four' put:4.
+        d at:'five' put:5.
+        d at:'six' put:6.
+        d at:'seven' put:7.
+        d at:'eight' put:8.
+        d at:'nine' put:9.
+        d keysAndValuesDo:[:k :v |
+            v odd ifTrue:[
+                d removeValue:v ifAbsent:nil 
+            ]
+        ].
+        d inspect
+    "
+
+    "DOES work:
+
+        |d|
+
+        d := Dictionary new.
+        d at:'one' put:1.
+        d at:'two' put:2.
+        d at:'three' put:3.
+        d at:'four' put:4.
+        d at:'five' put:5.
+        d at:'six' put:6.
+        d at:'seven' put:7.
+        d at:'eight' put:8.
+        d at:'nine' put:9.
+        d keysAndValuesDo:[:k :v |
+            v odd ifTrue:[
+                d saveRemoveValue:v 
+            ]
+        ].
+        d inspect
+    "
+
+    "Created: 1.3.1996 / 21:17:10 / cg"
+    "Modified: 1.3.1996 / 21:23:04 / cg"
 ! !
 
 !Dictionary methodsFor:'copying'!
@@ -838,5 +1056,5 @@
 !Dictionary class methodsFor:'documentation'!
 
 version
-    ^ '$Header: /cvs/stx/stx/libbasic/Dictionary.st,v 1.31 1995-11-24 13:02:02 cg Exp $'
+    ^ '$Header: /cvs/stx/stx/libbasic/Dictionary.st,v 1.32 1996-03-01 20:25:46 cg Exp $'
 ! !