Search code examples
javahashmapkey

Java - Two hashmap keys that link to same object value?


I have two ways I want to access an object...

One is through an UUID.

One is through a display name.

I currently store these maps in memory as I want to minimize the amount of work for the CPU.

One way that I could achieve this via looping through all Cosmetic objects and matching the display name to return the corresponding Cosmetic object. However, these operations could be run many times per second so would cause way too much stress on CPU.

So, I currently have it setup like this:

    private Map<String, Cosmetic> cosmeticIDs;
    private Map<String, WeakReference<Cosmetic>> cosmeticsDisplayNames;
    Cosmetic cosmetic = new Cosmetic(UUID, displayName);
    cosmeticIDs.put(cosmetic.getID(), cosmetic);
    cosmeticsDisplayNames.put(cosmetic.getDisplayName(), new WeakReference<>(cosmeticIDs.get(cosmetic.getID())));

These maps are loaded on application startup and never change.

Is there a better way of doing this? Seems like I am currently wasting a lot of memory.


Solution

  • Your WeakReference does absolutely nothing here. The object you are weakly referring to is strongly held by the cosmeticIDs hashmap and therefore cannot be collected, making the WeakReference useless here. Just make that second map a Map<String, Cosmetic>.

    Seems like I am currently wasting a lot of memory.

    All non-primitives are references in java. Cosmetic is, itself, already a reference. That second map of your maps references to strings (so, a 64-bit pointer probably, depends on many factors; the bit-ness of your CPU, the JVM, whether CompressedOOPS is enabled and so forth, but let's say 64 bits), to a reference to a cosmetic, so, another 64-bit value.

    Let's say you have 10,000 cosmetics - those things are already in memory for some reason (example: Because cosmeticIDs exists).

    That cosmeticsDisplayNames map therefore requires 10,000 * (64 + 64) bits of memory, more or less (it's a bit more than that, the hashmap needs some space for its own bookkeeping, of course). So, 160,000 bytes, or 160k. Your average system with, say, 8GB of RAM, that means it's 0.001907% of your total RAM.

    Seems like I am currently wasting a lot of memory.

    That'd be a no, then.

    However, these operations could be run many times per second so would cause way too much stress on CPU.

    You come across as someone with no clue but very worried about performance. You are jumping around from 'concerns about stressing the CPU' and 'concerns about wasting memory'.

    Stop doing this. Computers are extremely fast and as you can see this stuff costs less than one ten-thousandth to do. You're going to 'stress your CPU' if you look up cosmetics a few million times a second. If you're doing that, well, yeah, your computer is serving up a site that thousands of people are visiting simultaneously, it's doing its job.

    Worrying about performance is ruinous to programming. You will tie yourself into knots trying to 'find the most performant way', but, computers are extremely complicated, and generally your instincts about what would perform better are just flat out wrong.

    The consequence is that the 'cleanest' code, defined as: Easiest to read, easiest to understand, lowest odds of misleading, abstracted and easy to connect to other parts of the codebase because of that, easy to test, robust under future change requests which will inevitably arrive - that is the fastest code.

    In practice, 99% of the resources (CPU and RAM) are spent on less than 1% of the code. However, it's usually not possible to guess what that 1% is. So, you build your app without worrying about performance in detail (i.e. you do exactly the opposite of what you're doing with this SO question), then launch it. If it is slower than you thought it would be (big if), you run a profiler which tells you where that 1% is - the 'hot path'. You then optimize the heck out of that. Which is easy if your codebase is 'clean' as per the definition above, and very difficult if it wasn't, hence, 'better for performance' extremely finicky, badly abstracted, hard to integrate code is slower - because it complicates optimizing the hot path which is the only thing that's important.