PERF: Cache hashing of categories
Hashing of the dtype is used in numerous places of the code base. For example it is used in concat operations to determine whether all objects have the same dtype. For `CategoricalDtype` this operation can be extremely costly since it requires hashing the array/list of categories.

The following example shows the impact of this caching operation nicely since `pd.concat` hashes all dtypes at least twice

```python
import string
import pandas as pd


def make_df():
    return pd.DataFrame(
        {
            "A": [
                string.ascii_letters[ix : (ix + 10) % len(string.ascii_letters)]
                for ix in range(10000)
            ],
            "B": [
                string.ascii_lowercase[ix : (ix + 10) % len(string.ascii_lowercase)]
                for ix in range(10000)
            ],
        }
    ).astype({"A": "category", "B": "category"})


dfs = [make_df() for _ in range(100)]

```


```python
# without caching
%time pd.concat(dfs)
CPU times: user 74.1 ms, sys: 6.32 ms, total: 80.4 ms
Wall time: 80.2 ms

# with caching
%time pd.concat(dfs)
CPU times: user 46.1 ms, sys: 6.65 ms, total: 52.7 ms
Wall time: 52.1 ms
```

Note: On an earlier version (I believe it was 1.1.X but I need to check again) this caching brought this concat down to ~30ms but I haven't identified this _regression_, yet


- [x] closes N/A
- [x] N/A
- [x] Ensure all linting tests pass, see [here](https://pandas.pydata.org/pandas-docs/dev/development/contributing.html#code-standards) for how to run them
- [x] whatsnew entry
