I am wondering about the following question. To simulate an array via a hash table, one can simply set the keys of the hash table to be the indices of the array, and set the value of each key to be the value of the array at that index. In this way, it would seem that anything an array can do, a hash table can do, as well. This raises the question, is there any drawback, perhaps in terms of time or space complexity, to doing so? I'd love to hear your thoughts about this question.
If there were no collisions, and we ignore the CPU cost of hashing the key, and we don't store any of the hash codes, the hashtable would consume the same amount of space.
However, in practice, we have collisions, so we need a system to deal with that, which requires more space. Most linked list collision handling hash tables use integer indices into arrays, instead of pointers, to reduce space and increase memory locality. You will need at least one more array then, for the pool of linked list structures. There are many collision handling algorithms, but they all require additional storage.
If the hashes are expensive to compute, and the keys do not store hashes themselves, you need extra memory to store hash codes.
Finally, you have to deal with poor distributions, and potentially poor hash functions, if you have no control over the hash functions. In .net hash functions tend to be particularly poor, and I imagine they are in Java as well, since the people writing the key structure typically have to write the hash function, and the internet abounds with poor advice in this area. Therefore you can look forward to poor distributions, which leads to large numbers of collisions, and additional space needed to reduce those collisions.
The first disadvantage is that you are storing more stuff in memory. Instead of just storing a bunch of objects, you're storing a bunch of objects and indices. This just wastes space, and may slow the program down (especially if the data gets too big for the processor cache).
The second disadvantage is that it makes it more complicated (and hence slower) to do the things that arrays are good at.
Iterating through arrays is trivial. You should already know the address of the first element. Finding the address of the next element just requires adding the size of one object to the address. That's just one "add" instruction.
Finding the nth element in an array is almost as trivial. Start with the address of the first element, and add (n times the size of one object). So one "multiply" and one "add".