True, why because we do have the concept of keys and values in mappings but they are not really being set inside the mapping, only its
keccak256 hash is used to look up the value.
Why they use
keccak256 hash instead of actual value in mappings (As per docs) :
Types that do not occupy the full 32 bytes might contain “dirty higher order bits”. This is especially important if you access
msg.data - it poses a malleability risk: You can craft transactions that call a function
f(uint8 x) with a raw byte argument of
0xff000001 and with
0x00000001. Both are fed to the contract and both will look like the number
1 as far as
x is concerned, but msg.data will be different, so if you use
keccak256(msg.data) for anything, you will get different results.
If you want to read out some more info and want to understand how it works internally, you should try reading -> How you can clear Mappings ?