配列は非常に単純なデータ構造です。メモリ内では、単純な連続ブロックです。配列内の各項目が 4 バイトで、配列には 100 要素分のスペースがあるとします。次に、配列はメモリ内の連続した 400 バイトであり、配列に割り当てられた変数は最初の要素へのポインターです。これがメモリの 10000 番地にあるとします。
配列の要素 #3 にアクセスすると、次のようになります。
myarray[3] = 17;
...何が起こるかは非常に単純です: 要素のサイズ (4 バイト) を 3 倍した値がベース ポインターに追加されます。この例では、10000 + 3 * 4 = 10012 です。次に、アドレス 10012 にある 4 バイトに書き込むだけです。簡単な計算です。
ハッシュテーブルは基本的なデータ構造ではありません。さまざまな方法で実装できますが、単純なものは 256 個のリストの配列かもしれません。次に、ハッシュテーブルにアクセスするときは、まずキーのハッシュを計算し、次に配列内の正しいリストを検索し、最後にリストをたどって正しい要素を見つける必要があります。これは、はるかに複雑なプロセスです。
単純な配列は常にハッシュテーブルよりも高速です。あなたが引用したテキストが得ているのは、データが非常にまばらな場合...この単純な計算を行うには非常に大きな配列が必要になる可能性があるということです。その場合、ハッシュテーブルを保持するために使用するメモリスペースを大幅に減らすことができます。
文字が Unicode (それぞれ 2 バイト) であるかどうかを検討してください。それは 65536 の可能な文字です。また、256 文字以下の文字列についてのみ話しているとします。配列でこれらの文字を数えるには、64K の要素を持つ配列を作成し、それぞれ 1 バイト... 64K のメモリを使用する必要があります。一方、上記のように実装されたハッシュテーブルは、リスト ポインターの配列に 4*64 バイトしか必要とせず、リスト要素ごとに 5 ~ 8 バイトしか必要としません。したがって、たとえば 64 個の一意の Unicode 文字を使用して 256 文字の文字列を処理する場合、合計で最大 768 バイトを使用します。これらの条件下では、ハッシュテーブルが使用するメモリははるかに少なくなります。しかし、それは常に遅くなります。
最後に、あなたが示した単純なケースでは、おそらくラテンアルファベットについて話しているだけなので、小文字を強制すると、26要素だけの配列を持ち、それらを必要なだけ大きくして、いくつでも数えることができます必要に応じて文字を入力します。それが 40 億であっても、必要なのは 26 * 4 = 104 文字の配列だけです。ですから、間違いなくここに行く方法です。