0

Sorry if this is a noob question, but I am scratching my head how to add values to the following map inside a for loop:

private void filterSnakes() {
    Map<Integer, Map<Integer, Integer>> mapSnakes
            = new HashMap<Integer, Map<Integer, Integer>>();

    int coutner = 0;

    for(int i = 0; i < mGameAssets.length; i++)
    {
        if(mGameAssets[i].getAssetType().isSnake()){ //check if the asset is snake
             mapSnakes.put(++coutner, i,mGameAssets[i].getDamage() );
        }
    }
}

I mean how can I put a new entry in the mapSnakes, the key part is ok but how about the Value part which itself is a Map?

2
  • Is there any reason for using a hashmap instead of an array if you will use incremental numbers to index it? Commented Apr 20, 2014 at 0:11
  • @fejese Just ease of access to the content I guess. Commented Apr 20, 2014 at 0:12

3 Answers 3

2

You need to create or fetch the nested map first:

++counter;
if (!mapSnakes.containsKey(counter)) {
  mapSnakes.put(counter, new HashMap<Integer, Integer>());
}

mapSnakes.get(counter).put(i,mGameAssets[i].getDamage());

In the above code you create the nested map if it does not exist. Next you get the map with mapSnakes.get(counter) and add stuff to the nested map.

Sign up to request clarification or add additional context in comments.

2 Comments

This is a good general strategy, although in the OP's example mapSnakes is created new and is empty and i never repeats; containsKey() will always be false and "fetch/create" will always be "create".
@JasonC yup, good catch. I just went for a general approach where you have to put several values into a nested map without a specific order of keys
2

You have declared the map as mapping from an integer to a Map. Therefore

    mapSnakes.put(++counter, i,mGameAssets[i].getDamage() );

needs to be something like this:

    Map<Integer, Integer> submap = ...
    mapSnakes.put(++counter, submap);

... or possibly something like @dimoniy's answer. (It is not clear, because you don't actually specify how you want the data structure to be initialized. I could guess, but I'm not playing that game this morning.)


The other points to be made are:

  • This looks like a poor OO design. Maps of maps of basic types etcetera in Java has "code smell". Usually it means that you should have defined some application classes.

  • Maps are rather expensive data structures, both in memory usage and in time. And whenever you have to use Integer rather int, you incur overheads due to boxing and unboxing.

Comments

1

You have to separate this into two steps:

  1. Create the map you want to insert.
  2. Insert it into mapSnakes.

Like this:

if(mGameAssets[i].getAssetType().isSnake()){ //check if the asset is snake
    // Step 1
    Map<Integer,Integer> assetMap = new HashMap<Integer,Integer>();
    assetMap.put(i, mGameAssets[i].getDamage());
    // Step 2
    mapSnakes.put(++coutner, assetMap);
}

Although your design looks a little odd, are you sure this is what you want to be doing?


Responding to your comment, you say you want to know two things:

  • How many snakes are in mGameAsset.
  • What indices they are and what their damage is.

You could use a single map for that, a map that maps indices to the snake itself, e.g. assuming your assets are of class Asset:

private void filterSnakes () {

    // maps asset index => snake
    Map<Integer,Asset> snakes = new HashMap<Integer,Asset>(); 

    // find all the snakes:
    for (int i = 0; i < mGameAssets.length; ++ i) {
        if (mGameAssets[i].getAssetType().isSnake())
            snakes.put(i, mGameAssets[i]);
    }

    // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

    // now we can process their indices and damage, for example:
    for (Map.Entry<Integer,Asset> snakeEntry : snakes.entrySet()) {
        int index = snakeEntry.getKey();
        int damage = snakeEntry.getValue().getDamage();
        System.out.println("Snake at " + index + " damage is " + damage);
    }

    // and we can find the number of snakes too:
    int snakeCount = snakes.size();
    System.out.println("There are " + snakeCount + " snakes.");

    // bonus: we can even just get a generic collection of the snakes:
    Collection<Asset> snakesOnly = snakes.values();

}

Use a LinkedHashMap if you want to preserve insertion order instead.

Another option is to use an ArrayList or some other List instead of a Map; if you do that you'll have to make some small class that can hold an index and a snake (similar to a Map.Entry<Integer,Asset>, then you can create one of those small classes for each snake entry and maintain a List of those. A bit more work but has the advantage of not having the overhead of a map (which may or may not matter to you).

1 Comment

Thanks for the solution. About the design, well I want to know how many snakes are in the mGameAsset array, and what is theire index in the array and the damage they have. can you suggest something better?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.