Skip to content

Commit 455ad49

Browse files
authored
Optimize alter (#404)
1 parent f836b79 commit 455ad49

File tree

2 files changed

+30
-7
lines changed

2 files changed

+30
-7
lines changed

Data/HashMap/Internal.hs

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,7 @@ module Data.HashMap.Internal
134134
, equalKeys1
135135
, lookupRecordCollision
136136
, LookupRes(..)
137+
, lookupResToMaybe
137138
, insert'
138139
, delete'
139140
, lookup'
@@ -655,6 +656,11 @@ lookup' h k m = case lookupRecordCollision# h k m of
655656
-- If a collision did not occur then it will have the Int value (-1).
656657
data LookupRes a = Absent | Present a !Int
657658

659+
lookupResToMaybe :: LookupRes a -> Maybe a
660+
lookupResToMaybe Absent = Nothing
661+
lookupResToMaybe (Present x _) = Just x
662+
{-# INLINE lookupResToMaybe #-}
663+
658664
-- Internal helper for lookup. This version takes the precomputed hash so
659665
-- that functions that make multiple calls to lookup and related functions
660666
-- (insert, delete) only need to calculate the hash once.
@@ -1265,11 +1271,19 @@ update f = alter (>>= f)
12651271
-- 'lookup' k ('alter' f k m) = f ('lookup' k m)
12661272
-- @
12671273
alter :: (Eq k, Hashable k) => (Maybe v -> Maybe v) -> k -> HashMap k v -> HashMap k v
1268-
-- TODO(m-renaud): Consider using specialized insert and delete for alter.
12691274
alter f k m =
1270-
case f (lookup k m) of
1271-
Nothing -> delete k m
1272-
Just v -> insert k v m
1275+
let !h = hash k
1276+
!lookupRes = lookupRecordCollision h k m
1277+
in case f (lookupResToMaybe lookupRes) of
1278+
Nothing -> case lookupRes of
1279+
Absent -> m
1280+
Present _ collPos -> deleteKeyExists collPos h k m
1281+
Just v' -> case lookupRes of
1282+
Absent -> insertNewKey h k v' m
1283+
Present v collPos ->
1284+
if v `ptrEq` v'
1285+
then m
1286+
else insertKeyExists collPos h k v' m
12731287
{-# INLINABLE alter #-}
12741288

12751289
-- | \(O(\log n)\) The expression @('alterF' f k map)@ alters the value @x@ at

Data/HashMap/Internal/Strict.hs

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -306,9 +306,18 @@ update f = alter (>>= f)
306306
-- @
307307
alter :: (Eq k, Hashable k) => (Maybe v -> Maybe v) -> k -> HashMap k v -> HashMap k v
308308
alter f k m =
309-
case f (HM.lookup k m) of
310-
Nothing -> HM.delete k m
311-
Just v -> insert k v m
309+
let !h = hash k
310+
!lookupRes = HM.lookupRecordCollision h k m
311+
in case f (HM.lookupResToMaybe lookupRes) of
312+
Nothing -> case lookupRes of
313+
Absent -> m
314+
Present _ collPos -> HM.deleteKeyExists collPos h k m
315+
Just !v' -> case lookupRes of
316+
Absent -> HM.insertNewKey h k v' m
317+
Present v collPos ->
318+
if v `ptrEq` v'
319+
then m
320+
else HM.insertKeyExists collPos h k v' m
312321
{-# INLINABLE alter #-}
313322

314323
-- | \(O(\log n)\) The expression (@'alterF' f k map@) alters the value @x@ at

0 commit comments

Comments
 (0)