Dictionary.st
author Claus Gittinger <cg@exept.de>
Sun, 07 Jun 1998 02:08:56 +0200
changeset 3522 291ec553ac33
parent 3319 b1afc98a76bb
child 3727 2b05022de748
permissions -rw-r--r--
restart of restartableProcesses must be done after reinitialization of the event dispatcher.

"
 COPYRIGHT (c) 1991 by Claus Gittinger
	      All Rights Reserved

 This software is furnished under a license and may be used
 only in accordance with the terms of that license and with the
 inclusion of the above copyright notice.   This software may not
 be provided or otherwise made available to, or used by, any
 other person.  No title to or ownership of the software is
 hereby transferred.
"

Set subclass:#Dictionary
	instanceVariableNames:'valueArray'
	classVariableNames:''
	poolDictionaries:''
	category:'Collections-Unordered'
!

!Dictionary class methodsFor:'documentation'!

copyright
"
 COPYRIGHT (c) 1991 by Claus Gittinger
	      All Rights Reserved

 This software is furnished under a license and may be used
 only in accordance with the terms of that license and with the
 inclusion of the above copyright notice.   This software may not
 be provided or otherwise made available to, or used by, any
 other person.  No title to or ownership of the software is
 hereby transferred.
"
!

documentation
"
    a Dictionary is (conceptionally) a set of Associations storing key-value pairs.
    (The implementation uses two arrays to store the keys and values separately.)
    Searching for an element is done using a hash into the key array.
    Another way of looking at a dictionary is as a array which uses
    arbitrary access keys (i.e. not just integers as arrays do).

    Since the keys are unordered, no internal element order is defined
    (i.e. enumerating them may return elements in any order - even changing
     over time).

    Many methods for searching and hashing are inherited from Set.

    [Instance variables:]

        keyArray        <Array>         (from Set) the keys

        valueArray      <Array>         the values ('valueArray at:index' corresponds
                                        to the value stored under 'keyArray at:index')

    Performance hints: 
      since the dictionary does not really store associations internally,
      it is less efficient, to store/retrieve associations. The reason is
      that these assocs are created temporarily in some extract methods. 
      I.e. 'at:key put:value' is faster than 'add:anAssoc' 
      and 'keysAndValuesDo:' is faster than 'associationsDo:' etc.

      If only symbols or smallIntegers are used as keys, use IdentityDictionaries
      for slightly better performance, since both hashing and comparison is faster.

      If you have a rough idea how big the dictionary is going to grow,
      create it using #new: instead of #new. Even if the size given is a
      poor guess (say half of the real size), there is some 20-30% performance
      win to expect, since many resizing operations are avoided when associations
      are added.

    [See also:]
        Set, IdentityDictionary, IdentitySet, WeakIdentitySet and
        WeakIdentityDictionary

    [author:]
        Claus Gittinger
"
! !

!Dictionary class methodsFor:'instance creation'!

withKeys:keyArray andValues:valueArray
    "return a new instance where keys and values are taken from
     the argumentArrays."

    |newDict sz "{ Class: SmallInteger }"|

    sz := keyArray size.
    newDict := self new:sz.
    keyArray with:valueArray do:[:key :value |
	newDict at:key put:value
    ].
    ^ newDict

    "
     Dictionary withKeys:#('one' 'two' 'three' 'four')
	       andValues:#(1 2 3 4)
    "
!

withKeysAndValues:anArray
    "return a new instance where keys and values are taken from alternating
     elements of anArray"

    |newDict sz "{ Class: SmallInteger }"|

    sz := anArray size.
    newDict := self new:(sz // 2).
    1 to:sz by:2 do:[:i |
	newDict at:(anArray at:i) put:(anArray at:i+1)
    ].
    ^ newDict

    "
     Dictionary withKeysAndValues:#('one' 1 'two' 2 'three' 3 'four' 4)
    "
! !

!Dictionary class methodsFor:'STBroker'!

unmarshallCorbaStructureType: anODLTypeStructure using: aMarshaller

	^aMarshaller
		unmarshallDictionaryTypeStructure: anODLTypeStructure
			class: self! !

!Dictionary methodsFor:'accessing'!

associationAt:aKey
    "return an association consisting of aKey and the element indexed 
     by aKey - 
     report an error, if no element is stored under aKey"

    ^ Association key:aKey value:(self at:aKey)
!

associationAt:aKey ifAbsent:exceptionBlock
    "return an association consisting of aKey and the element indexed by aKey - 
     return result of exceptionBlock if no element is stored under aKey"

    ^ Association key:aKey value:(self at:aKey ifAbsent:[^ exceptionBlock value])
!

associations
    "return an ordered collection containing the receivers associations."

    |coll|

    coll := OrderedCollection new:(keyArray size).
    self associationsDo:[:assoc | coll add:assoc].
    ^ coll
!

at:aKey
    "return the element indexed by aKey - report an error if none found"

    |index|

    aKey isNil ifTrue:[
	"/ nil is not allowed as key
	^ self errorInvalidKey:aKey
    ].

    "/
    "/ I could have written:
    "/ index := self find:aKey ifAbsent:[^ self errorKeyNotFound:aKey]
    "/ but the code below is slighlty more efficient, since it avoids
    "/ a block creation - thus speeding up the good case.
 
    index := self find:aKey ifAbsent:0.
    index ~~ 0 ifTrue:[
        ^ valueArray basicAt:index
    ].
    "no such key"
    ^ self errorKeyNotFound:aKey
!

at:aKey ifAbsent:exceptionBlock
    "return the element indexed by aKey - 
     return result of exceptionBlock if no element is stored under aKey"

    |index|

    aKey isNil ifTrue:[
	"/ nil is not allowed as key
	"/
	"/ previous versions of ST/X raised an error
	"/ here. However, there seem to exist applications
	"/ which depend on getting the exceptionBlocks value
	"/ in this case ... well ...
	"/ ^ self errorInvalidKey:aKey
	^ exceptionBlock value
    ].

    "/ I could have written:
    "/ index := self find:aKey ifAbsent:[^ exceptionBlock value]
    "/ but the code below is slighlty more efficient, since it avoids
    "/ a block creation - thus speeding up the good case.

    index := self find:aKey ifAbsent:0.
    index ~~ 0 ifTrue:[
        ^ valueArray basicAt:index
    ].
    ^ exceptionBlock value.
!

at:aKey ifAbsentPut:valueBlock
    "return the element indexed by aKey if present,
     if not present, store the result of evaluating valueBlock
     under aKey and return it.
     WARNING: do not add elements while iterating over the receiver.
              Iterate over a copy to do this."

    |index "{ Class: SmallInteger }"
     newValue|

    aKey isNil ifTrue:[
        "nil is not allowed as key"
        ^ self errorInvalidKey:aKey
    ] ifFalse:[
        index := self findKeyOrNil:aKey.
        (keyArray basicAt:index) notNil ifTrue:[
            "/ already present
            ^ valueArray at:index.
        ].
        "/ a new one
        newValue := valueBlock value.
        keyArray basicAt:index put:aKey.
        valueArray basicAt:index put:newValue.
        tally := tally + 1.
        self fullCheck.
    ].
    ^ newValue

    "
     |d|

     d := Dictionary new.
     Transcript showCR:(d at:'foo' ifAbsentPut:'bar').
     Transcript showCR:(d at:'foo2' ifAbsentPut:'bar2').
     Transcript showCR:(d at:'foo' ifAbsentPut:'barX').
     Transcript showCR:(d at:'foo2' ifAbsentPut:'bar2X').
    "

    "Created: / 23.1.1998 / 18:28:26 / cg"
    "Modified: / 26.2.1998 / 19:10:09 / stefan"
!

at:aKey put:anObject
    "add the argument anObject under key, aKey to the receiver.
     Return anObject (sigh).
     WARNING: do not add elements while iterating over the receiver.
              Iterate over a copy to do this."

    |index "{ Class: SmallInteger }"|

    aKey isNil ifTrue:[
        "nil is not allowed as key"
        self errorInvalidKey:aKey
    ] ifFalse:[
        anObject isNil ifTrue:[
            self removeKey:aKey ifAbsent:nil.
            ^ nil
        ].
        index := self findKeyOrNil:aKey.
        (keyArray basicAt:index) notNil ifTrue:[
            "/ key already present
            valueArray basicAt:index put:anObject.
            ^ anObject
        ].
        "/ a new key
        keyArray basicAt:index put:aKey.
        valueArray basicAt:index put:anObject.
        tally := tally + 1.

        self fullCheck.
    ].
    ^ anObject

    "Modified: 30.1.1997 / 14:59:10 / cg"
!

keyAtEqualValue:aValue
    "return the key whose value is equal (i.e. using #= for compare)
     to the argument, nil if none found.
     This is a slow access, since there is no fast reverse mapping.
     NOTICE:
	The value is searched using equality compare; 
	use #keyAtValue: to compare for identity."

    ^ self keyAtEqualValue:aValue ifAbsent:[nil]
!

keyAtEqualValue:aValue ifAbsent:exceptionBlock
    "return the key whose value is equal (i.e. using #= for compare) 
     to the argument, if not found, return the value of exceptionBlock.
     This is a slow access, since there is no fast reverse mapping.
     NOTICE:
	The value is searched using equality compare; 
	use #keyAtValue:ifAbsent: to compare for identity."

    |idx|

    idx := valueArray indexOf:aValue.
    idx ~~ 0 ifTrue:[
	^ keyArray at:idx
    ].

"/  keyArray keysAndValuesDo:[:index :aKey |
"/      (aKey notNil and:[aKey ~~ DeletedEntry]) ifTrue:[
"/          (valueArray at:index) = aValue ifTrue:[^ aKey].  
"/      ].
"/  ].

    ^ exceptionBlock value
!

keyAtValue:aValue
    "return the key whose value is identical (i.e. using #== for compare)
     to the argument, nil if none found.
     This is a slow access, since there is no fast reverse mapping.
     NOTICE:
	The value is searched using identity compare; 
	use #keyAtEqualValue: to compare for equality."

    ^ self keyAtValue:aValue ifAbsent:[nil]
!

keyAtValue:aValue ifAbsent:exceptionBlock
    "return the key whose value is identical (i.e. using #== for compare) 
     to the argument, if not found, return the value of exceptionBlock.
     This is a slow access, since there is no fast reverse mapping.
     NOTICE:
        The value is searched using identity compare; 
        use #keyAtEqualValue:ifAbsent: to compare for equality."

    |idx|

    idx := valueArray identityIndexOf:aValue startingAt:1.
    idx ~~ 0 ifTrue:[
        ^ keyArray at:idx
    ].

"/  keyArray keysAndValuesDo:[:index :aKey |
"/      (aKey notNil and:[aKey ~~ DeletedEntry]) ifTrue:[
"/          (valueArray at:index) == aValue ifTrue:[^ aKey].  
"/      ].
"/  ].

    ^ exceptionBlock value

    "Modified: 20.3.1997 / 15:58:49 / cg"
!

keys
    "return a collection containing all keys of the receiver"

    |keySet|

    keySet := self emptyCollectionForKeys.
    keyArray do:[:key | 
	(key notNil and:[key ~~ DeletedEntry]) ifTrue:[
	    keySet add:key
	]
    ].
    ^ keySet
!

values
    "return a collection containing all values of the receiver"

"old:
kk: this fails if the receiver contains nils
    ^ valueArray asBag
new:
"
    |aCollection|

    aCollection := OrderedCollection new:valueArray size.
    self do:[:value| aCollection add:value].
    ^ aCollection
! !

!Dictionary methodsFor:'adding & removing'!

add:anAssociation
    "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"
!

addPairsFrom:aSequenceableCollection
    "merge all key-value pairs from aDictionary into the receiver."

    aSequenceableCollection pairWiseDo:[:key :value |
        self at:key put:value.
    ]

    "Modified: 1.3.1996 / 21:24:03 / 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.

     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.
    ]

    "Modified: 1.3.1996 / 21:24:03 / cg"
!

declareAllFrom:aDictionary
    "merge all key-value pairs from aDictionary into the receiver."

    aDictionary keysAndValuesDo:[:key :value |
        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 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.

     WARNING: do not remove elements while iterating over the receiver.
	      See #saveRemoveKey: to do this."

    |key|

    key := assoc key.
    ^ Association key:key value:(self removeKey:key)

    "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.

     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.

     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
    ].

    "/   below, I could have written:
    "/      index := self find:aKey ifAbsent:[^ aBlock value]
    "/   but the code below is slighlty more efficient, since it avoids
    "/   a garbage block creation - thus speeding up the good case.

    index := self find:aKey ifAbsent:0.
    index == 0 ifTrue:[^ aBlock value].

    oldValue := valueArray basicAt:index.

    valueArray basicAt:index put:nil.
    keyArray basicAt:index put:nil.

    tally := tally - 1.
    tally == 0 ifTrue:[
	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
    ].
    ^ oldValue

    "Modified: 1.3.1996 / 21:21:01 / cg"
!

removeValue:aValue ifAbsent:aBlock
    "remove (first) the association to aValue from the collection,
     return the key under which it was stored previously.
     If it was not in the collection return result from evaluating aBlock.
     The value is searched using equality compare here,
     but identity compare in the IdentityDictionary subclass.

     Notice, this does a linear search through the values and may
     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}"|

	    (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: 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:'converting'!

fromLiteralArrayEncoding:encoding
    "read my values from an encoding.
     The encoding is supposed to be of the form: 
        (Dictionary key1 val1 ... keyN valN)"

    |idx|

    2 to:encoding size by:2 do:[:i |
        |key val|

        key := encoding at:i.
        val := encoding at:i+1.
        self at:key put:val
    ].

    "
     Dictionary new fromLiteralArrayEncoding:#(Dictionary 'hello' 'world' 1 'foo')
    "

    "Created: / 30.1.1998 / 04:30:47 / cg"
!

literalArrayEncoding
    |coll|

    coll := OrderedCollection new.
    coll add:(self class name asSymbol).
    self keysAndValuesDo:[:key :value |
        coll add:key literalArrayEncoding.    
        coll add:value literalArrayEncoding.    
    ].
    ^ coll asArray

    "Created: / 30.1.1998 / 04:28:31 / cg"
    "Modified: / 30.1.1998 / 04:31:23 / cg"
! !

!Dictionary methodsFor:'copying'!

postCopy
    "have to copy the valueArray too"

    super postCopy.
    valueArray := valueArray shallowCopy
! !

!Dictionary methodsFor:'enumerating'!

allKeysDo:aBlock
    "perform the block for all keys in the collection.
     Obsolete: use keysDo: for ST-80 compatibility."

    self obsoleteMethodWarning:'please use #keysDo:'.
    ^ super do:aBlock

    "Modified: 20.4.1996 / 11:22:01 / cg"
!

associationsCollect:aBlock
    "for each key-value pair in the receiver, evaluate the argument, aBlock
     and return a collection with the results.

     See also:
        #keysAndValuesCollect: (which passes separate keys & values)
        #collect:              (which only passes values)

     This is much like #keysAndValuesCollect:, but aBlock gets the
     key and value as a single association argument.
     #keysAndValuesCollect: and is a bit faster therefore (no intermediate objects).

     WARNING: do not add/remove elements while iterating over the receiver.
              Iterate over a copy to do this."

    |newCollection|

    newCollection := OrderedCollection new.
    self keysAndValuesDo:[:key :value |
        newCollection add:(aBlock value:(Association key:key value:value))
    ].
    ^ newCollection

    "
     |ages|

     ages := Dictionary new.
     ages at:'cg' put:37.
     ages at:'ca' put:33.
     ages at:'sv' put:36.
     ages at:'tk' put:28.
     ages associationsCollect:[:assoc | 
                assoc key , '''s age is ' , assoc value printString]
    "

    "Modified: 20.4.1996 / 11:31:27 / cg"
!

associationsDo:aBlock
    "perform the block for all associations in the collection.

     See also:
        #do:              (which passes values to its block)
        #keysDo:          (which passes only keys to its block)
        #keysAndValuesDo: (which passes keys&values)

     This is much like #keysAndValuesDo:, but aBlock gets the
     key and value as a single association argument.
     #keysAndValuesDo: and is a bit faster therefore (no intermediate objects).

     WARNING: do not add/remove elements while iterating over the receiver.
              Iterate over a copy to do this."

    |key n "{ Class: SmallInteger }"|

    tally == 0 ifTrue:[^ self].
    n := keyArray basicSize.
    1 to:n do:[:index |
        key := keyArray basicAt:index.
        (key notNil and:[key ~~ DeletedEntry]) ifTrue:[
            aBlock value:(Association key:key value:(valueArray basicAt:index))
        ]
    ]

    "Modified: 20.4.1996 / 11:31:39 / cg"
!

associationsReverseDo:aBlock
    "perform the block for all associations in the collection.
     Since dictionary does not define any order of its elements,
     this is the same as #associationsDo: here.
     Provided for protocol compatibility with OrderedDictionary"

    ^ self associationsDo:aBlock

    "Created: 28.2.1997 / 16:08:52 / cg"
!

associationsSelect:aBlock
    "return a new collection with all elements from the receiver, for which
     the argument aBlock evaluates to true. 
     The block gets keys and values as an association argument.

     See also: #keysAndValuesSelect: (which is slightly faster),
               #select: (which only passes the value)

     This is much like #keysAndValuesSelect:, but aBlock gets the
     key and value as a single association argument.
     #keysAndValuesSelect: and is a bit faster therefore (no intermediate objects).

     WARNING: do not add/remove elements while iterating over the receiver.
              Iterate over a copy to do this."

    |newCollection|

    newCollection := self species new.
    self keysAndValuesDo:[:key :value |
        (aBlock value:(Association key:key value:value)) ifTrue:[
            newCollection at:key put:value
        ]
    ].
    ^ newCollection

    "
     |ages|

     ages := Dictionary new.
     ages at:'cg' put:37.
     ages at:'ca' put:33.
     ages at:'sv' put:36.
     ages at:'tk' put:28.

     ages associationsSelect:[:assoc | 
                (assoc key startsWith:'c') or:[assoc value < 30]].
    "

    "Modified: 20.4.1996 / 11:31:15 / cg"
!

collect:aBlock
    "for each element in the receiver, evaluate the argument, aBlock
     and return a Bag with the results.

     See also:
        #associationsCollect:   (which passes key-value associations)
        #keysAndValuesCollect:  (which passes keys & values separately)

     WARNING: do not add/remove elements while iterating over the receiver.
              Iterate over a copy to do this."

    |newCollection|

    newCollection := Bag new.
    self do:[:each |
        newCollection add:(aBlock value:each)
    ].
    ^ newCollection

    "Modified: 20.4.1996 / 11:29:58 / cg"
!

do:aBlock
    "perform the block for all values in the collection.

     See also:
        #associationsDo:   (which passes key-value associations)
        #keysAndValuesDo:  (which passes keys & values separately)
        #keysDo:           (which passes keys only)

     WARNING: do not add/remove elements while iterating over the receiver.
              Iterate over a copy to do this."

    |key n "{ Class: SmallInteger }"|

    tally == 0 ifTrue:[^ self].
    n := keyArray basicSize.
    1 to:n do:[:index |
        key := keyArray basicAt:index.
        (key notNil and:[key ~~ DeletedEntry]) ifTrue:[
            aBlock value:(valueArray basicAt:index)
        ].
    ]

    "Modified: 20.4.1996 / 11:32:11 / cg"
!

keysAndValuesCollect:aBlock
    "for each key-value pair in the receiver, evaluate the argument, aBlock
     and return a collection with the results.

     See also:
        #associationsCollect:  (which passes keys->value pairs)
        #collect:              (which only passes values)

     This is much like #associationsCollect:, but aBlock gets the
     key and value as two separate arguments.
     #associationsCollect: is a bit slower.

     WARNING: do not add/remove elements while iterating over the receiver.
              Iterate over a copy to do this."

    |newCollection|

    newCollection := OrderedCollection new.
    self keysAndValuesDo:[:key :value |
        newCollection add:(aBlock value:key value:value)
    ].
    ^ newCollection

    "
     |ages|

     ages := Dictionary new.
     ages at:'cg' put:37.
     ages at:'ca' put:33.
     ages at:'sv' put:36.
     ages at:'tk' put:28.
     ages keysAndValuesCollect:[:name :age | 
                name , '''s age is ' , age printString]
    "

    "Modified: 20.4.1996 / 11:33:50 / cg"
!

keysAndValuesDo:aTwoArgBlock
    "evaluate the argument, aBlock for every element in the collection,
     passing both key and element as arguments.

     See also:
        #associationsDo:       (which passes keys->value pairs)
        #do:                   (which only passes values)
        #keysDo:               (which only passes keys)

     This is much like #associationsDo:, but aBlock gets the
     key and value as two separate arguments.
     #associationsDo: is a bit slower.

     WARNING: do not add/remove elements while iterating over the receiver.
              Iterate over a copy to do this."

    |key n "{ Class: SmallInteger }"|

    tally == 0 ifTrue:[^ self].
    n := keyArray basicSize.
    1 to:n do:[:index |
        key := keyArray basicAt:index.
        (key notNil and:[key ~~ DeletedEntry]) ifTrue:[
            aTwoArgBlock value:key value:(valueArray basicAt:index)
        ].
    ]

    "Modified: 20.4.1996 / 11:33:42 / cg"
!

keysAndValuesSelect:aBlock
    "return a new collection with all elements from the receiver, for which
     the argument aBlock evaluates to true. 
     The block gets keys and values as separate arguments.

     See also: 
        #associationsSelect:    (which passes key-value pairs),
        #select:                (which only passes the value)

     This is much like #associationsSelect:, but aBlock gets the
     key and value as two separate arguments.
     #associationsSelect: is a bit slower.

     WARNING: do not add/remove elements while iterating over the receiver.
              Iterate over a copy to do this."

    |newCollection|

    newCollection := self species new.
    self keysAndValuesDo:[:key :value |
        (aBlock value:key value:value) ifTrue:[
            newCollection at:key put:value
        ]
    ].
    ^ newCollection

    "
     |ages|

     ages := Dictionary new.
     ages at:'cg' put:37.
     ages at:'ca' put:33.
     ages at:'sv' put:36.
     ages at:'tk' put:28.

     ages keysAndValuesSelect:[:name :age | 
                (name startsWith:'c') or:[age < 30]].
    "

    "Modified: 20.4.1996 / 11:34:29 / cg"
!

keysDo:aBlock
    "perform the block for all keys in the collection.

     See also:
        #associationsDo:   (which passes key-value associations)
        #keysAndValuesDo:  (which passes keys & values separately)
        #do:               (which passes values only)

     WARNING: do not add/remove elements while iterating over the receiver.
              Iterate over a copy to do this."

    ^ super do:aBlock

    "Modified: 20.4.1996 / 11:34:45 / cg"
!

select:aBlock
    "return a new collection with all elements from the receiver, for which
     the argument aBlock evaluates to true. The block gets the individual values
     as its single argument.

     See also: 
        #associationsSelect:            (which passes key->value associations),
        #keysAndValuesSelect:           (which passes key & value args)

     WARNING: do not add/remove elements while iterating over the receiver.
              Iterate over a copy to do this."

    |newCollection|

    newCollection := self species new.
    self keysAndValuesDo:[:key :value |
        (aBlock value:value) ifTrue:[
            newCollection at:key put:value
        ]
    ].
    ^ newCollection

    "
     |d|

     d := Dictionary new.
     d at:#foo put:#bar.
     d at:#bar put:#baz.
     d at:#baz put:#foo.

     d select:[:el | el startsWith:'b'].
    "

    "Modified: 20.4.1996 / 11:35:07 / cg"
! !

!Dictionary methodsFor:'inspecting'!

inspectorClass
    "redefined to use DictionaryInspector
     (instead of the default Inspector)."

    ^ DictionaryInspectorView
! !

!Dictionary methodsFor:'printing & storing'!

printElementsDo:aBlock
    "redefined, so #printOn: prints associations"

    ^ self associationsDo:aBlock

    "Created: / 20.1.1998 / 14:11:02 / stefan"
!

storeOn:aStream
    "output a printed representation (which can be re-read)
     onto the argument aStream"

    |isEmpty|

    thisContext isRecursive ifTrue:[
        ('Dictionary [error]: storeOn: of self referencing collection.') errorPrintCR.
        aStream nextPutAll:'#recursive'.
        ^ self
    ].

    aStream nextPutAll:'('.
    aStream nextPutAll:(self class name).
    aStream nextPutAll:' new'.
    isEmpty := true.
    self keysAndValuesDo:[:key :value |
        aStream nextPutAll:' at:'.
        key storeOn:aStream.
        aStream nextPutAll:' put:'.
        value storeOn:aStream.
        aStream nextPutAll:'; '.
        isEmpty := false
    ].
    isEmpty ifFalse:[aStream nextPutAll:' yourself'].
    aStream nextPut:$)

    "
     Dictionary new storeOn:Transcript

     (Dictionary new at:1 put:'hello'; yourself) storeOn:Transcript
    "

    "
     |d|
     d := Dictionary new.
     d at:1 put:'hello'.
     d at:'hello' put:#world.
     d storeOn:Transcript
    "

    "
     |d|
     d := Dictionary new.
     d at:1 put:'hello'.
     d at:'hello' put:#world.
     d at:2 put:d.
     d storeOn:Transcript
    "

    "Modified: 28.1.1997 / 00:37:46 / cg"
! !

!Dictionary methodsFor:'private'!

compareSame:element1 with:element2
    "compare two elements for being the same. Here, return true if the
     elements are equal (i.e. using #=).
     Redefinable in subclasses."

    ^ element1 = element2

    "Modified: 22.4.1996 / 17:34:27 / cg"
!

emptyCollectionForKeys
    "return an empty collection to hold keys. Here, a Set is returned.
     Redefinable in subclasses."

    ^ Set new:(self size)

    "Modified: 22.4.1996 / 17:35:17 / cg"
!

grow:newSize
    "grow the receiver to make space for at least newSize elements.
     To do this, we have to rehash into the new arrays.
     (which is done by re-adding all elements to a new, empty key/value array pair)."

    |key deletedEntry oldKeyArray oldValueArray n
     oldSize  "{ Class:SmallInteger }" 
     newIndex "{ Class:SmallInteger }" |

    oldKeyArray := keyArray.
    oldValueArray := valueArray.

    n := self class goodSizeFrom:newSize.
    oldSize := oldKeyArray size.
    n == oldSize ifTrue:[^ self].

    keyArray := self keyContainerOfSize:n.
    valueArray := self valueContainerOfSize:n.


    deletedEntry := DeletedEntry.
    1 to:oldSize do:[:index |
	key := oldKeyArray basicAt:index.
	(key notNil and:[key ~~ deletedEntry]) ifTrue:[
	    newIndex := self findNil:key.
	    keyArray basicAt:newIndex put:key.
	    valueArray basicAt:newIndex put:(oldValueArray basicAt:index).
	]
    ]
!

rehash
    "rehash contents - is done by re-adding all elements to a new, empty key/value array pair)."

    | oldKeyArray oldValueArray key 
      n        "{ Class:SmallInteger }"
      newIndex "{ Class:SmallInteger }" |

    oldKeyArray := keyArray.
    oldValueArray := valueArray.

    n := keyArray size.
    keyArray := self keyContainerOfSize:n.
    valueArray := self valueContainerOfSize:n.

    1 to:n do:[:index |
	key := oldKeyArray basicAt:index.
	(key notNil and:[key ~~ DeletedEntry]) ifTrue:[
	    newIndex := self findNil:key.
	    keyArray basicAt:newIndex put:key.
	    valueArray basicAt:newIndex put:(oldValueArray basicAt:index).
	]
    ]
!

rehashFrom:startIndex
    "rehash elements starting at index - after a remove.
     NOTE: this method is no longer needed; 
	   the trick using DeletedEntry avoids the need to do this time 
	   consuming operation, making remove pretty fast :-)
    "

    |key i length
     index "{ Class:SmallInteger }" |

    length := keyArray basicSize.
    index := startIndex.
    key := keyArray basicAt:index.
    [key notNil] whileTrue:[
	key ~~ DeletedEntry ifTrue:[
	    i := self findNil:key.
	    i == index ifTrue:[
		^ self
	    ].
	    keyArray basicAt:i put:key.
	    valueArray basicAt:i put:(valueArray basicAt:index).
	    keyArray basicAt:index put:nil.
	    valueArray basicAt:index put:nil.
	].
	index == length ifTrue:[
	    index := 1
	] ifFalse:[
	    index := index + 1.
	].
	key := keyArray basicAt:index.
    ]
!

setTally:count
    "initialize the contents array (for at least count slots)
     and set tally to zero.
     The size is increased to the next prime for better hashing behavior."

    |n|

    n := self class goodSizeFrom:count.
    keyArray := self keyContainerOfSize:n.
    valueArray := self valueContainerOfSize:n.
    tally := 0
!

valueContainerOfSize:n
    "return a container for values of size n.
     Extracted to make life of weak subclasses easier ..."

    ^ Array basicNew:n
! !

!Dictionary methodsFor:'searching'!

findFirstKey:aBlock
    "find and return the first key, for which evaluation of the argument, aBlock
     returns true; return nil if none is detected."

    self keysDo:[:key |
        (aBlock value:key) ifTrue:[^ key].
    ].
    ^ nil

    "Created: 5.6.1996 / 11:55:50 / stefan"
    "Modified: 8.10.1996 / 22:01:59 / cg"
! !

!Dictionary methodsFor:'testing'!

includes:anObject
    "return true, if the argument, aValue is stored in the dictionary,
     i.e. if there is an associaten, with aValue as value.
     This is a slow search, since there is no fast reverse mapping;
     the values have to be all scanned without any hashing.
     You need a special collection (or two Dictionaries) to get this
     reverse mapping fast."

"/ OLD:
"/    ^ self includesKey:(anObject key)
"/ NEW:

    ^ self includesValue:anObject

    "Modified: 22.4.1996 / 17:20:11 / cg"
!

includesAssociation:anAssociation
    "return true, if there is an association in the receiver with the
     same key and value as the argument, anAssociation.
     NOTICE: in contrast to #includes:, this compares both key and value."

    |val|

    val := self at:(anAssociation key) ifAbsent:[^ false].
    ^ self compareSame:val with:anAssociation value

    "Modified: / 5.3.1998 / 20:35:00 / cg"
!

includesKey:aKey
    "return true, if the argument, aKey is a key in the receiver"

    ^ (self find:aKey ifAbsent:0) ~~ 0
!

includesValue:aValue
    "return true, if the argument, aValue is stored in the dictionary,
     i.e. if there is an associaten, with aValue as value.
     This is a slow search, since there is no fast reverse mapping;
     the values have to be all scanned without any hashing.
     You need a special collection (or two Dictionaries) to get this
     reverse mapping fast."

    ^ valueArray includes:aValue
!

occurrencesOf:anObject
    "count & return how often anObject is stored in the dictionary.
     This counts values - not keys."

    ^ valueArray occurrencesOf:anObject
! !

!Dictionary class methodsFor:'documentation'!

version
    ^ '$Header: /cvs/stx/stx/libbasic/Dictionary.st,v 1.58 1998-03-05 19:36:28 cg Exp $'
! !