2

[updated code] (Sorry guys, I didn't provide the whole code, because in my experience large codes seem to "scare off" possible helpers.)

For an ExpandableListView I want to build a HashMap<String, ArrayList<String>> where String is the name of a category and ArrayList<String> the names of animals belonging to that category. I populate the HashMap as such:

HashMap<String, ArrayList<String>> map_groups_childs;
ArrayList<String> list_group_titles;

private void prepareListData(ArrayList<Id_triv_cat> search_results) {
    list_group_titles = new ArrayList<String>();                  // this is a list of group titles
    map_groups_childs = new HashMap<String, ArrayList<String>>(); // this is a map. each group title gets a list of its respective childs

    // temporary List of items for a certain category/group
    ArrayList<String> temp_childs_list = new ArrayList<String>();

    // "search_results" is an ArrayList of self defined objects each containing an ID, a name and a category name
    int count = search_results.size();
    int i_cat = 0;
    int i=0;

    // if category "i" is the same as the next category "i+1", add child to list
    for (i=0; i<count-1; i++) {
        // build group with category name
        list_group_titles.add(search_results.get(i).get_type());

        // while category does not change, add child to the temporary childs-array
        while (i<=count && search_results.get(i).get_type().equals(search_results.get(i+1).get_type())) {
            temp_childs_list.add(search_results.get(i).get_name());
            i++;
        }

        // must be done, as the while loop does not get to the last "i" of every category
        temp_childs_list.add(search_results.get(i).get_name());

        Log.i("DEBUG", temp_childs_list.size());      // --> returns always more than 0
        Log.i("DEBUG", temp_childs_list.toString());  // --> returns always something like [word1, word2, word3, ...]
        Log.i("DEBUG", list_group_titles.get(i_cat)); // --> returns always a single word like "Insekten"

        // add [group_title:list_of_childs] to map
        map_groups_childs.put(list_group_titles.get(i_cat++), temp_childs_list);

        // clear temp_list, otherwise former category's species will be added to new category
        temp_childs_list.clear();
    }
Log.i("DEBUG", map_groups_childs.containsKey("Insekten"));  // --> returns true
Log.i("DEBUG", map_groups_childs.size());                   // --> returns 10
Log.i("DEBUG", map_groups_childs.get("Insekten").size());   // --> returns 0
Log.i("DEBUG", map_groups_childs.toString());               // --> returns {Insekten=[], Reptilien=[], ...}
}

The use of the same i in the for- and while-loop may seem wrong or confusing, but it is okay. No i is skipped in any way or used twice.

All keys I put in the HashMap are there, but the ArrayList I want to get with (for example) map_groups_childs.get("Insekten") is empty. What am I doing wrong?

3
  • 1
    The bug is in the code you chose not to show. Commented Oct 25, 2014 at 22:00
  • Your example code is useless, because it gives no hint, were your problem could be. maybe you added a newly generated arraylist instead of adding the filled one. Commented Oct 25, 2014 at 22:03
  • "I didn't provide the whole code, because in my experience large codes seem to "scare off" possible helpers." No, what you are supposed to do is create an MCVE by reducing the problem to its most minimalistic form. Commented Oct 26, 2014 at 22:51

3 Answers 3

2
    ...

    map_groups_childs.put(..., temp_childs_list);

    temp_childs_list.clear();
}

Objects are passed as a reference in Java. You are always putting the same List in to the Map and clearing it after every iteration. Thus every value in the Map points to the same List which is empty.

What you probably need is something like this:

for( ... ) {
    List<String> tempChildsList = new ArrayList<>();

    ...

    mapGroupChilds.put(..., tempChildsList);
}

Thus a new List is created on every iteration.

I also agree with @CandiedOrange your code is a mess and probably overly complex. In general the point of abstractions like List and Map is to not access things by counting numerical indexes all the time.

Note that in Java, the convention is that identifiers for variables are camelCase, not under_scored.

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

2 Comments

+1 for camelCase. Underscores are for all cap finals LIKE_THIS.
That's exactly it! Needless to say, I'm not an expert when it comes to Java. The concept of references usually is not important to me, that's why I would have never seen this.
2

Near as I can tell what you need are the 5 lines in the second group below

    HashMap<String, ArrayList<String>> map_groups_childs = new HashMap<String, ArrayList<String>>();
    ArrayList<String> list_group_titles = new ArrayList<String>();
    ArrayList<String> temp_childs_list  = new ArrayList<String>();

    list_group_titles.add("insects");
    temp_childs_list.add("Swallowtail");
    temp_childs_list.add("Small White");
    temp_childs_list.add("Large White");
    temp_childs_list.add("Silverfish");

    int k = 0; 

    Log.i("DEBUG", list_group_titles.get(k)); // --> returns "insects"
    Log.i("DEBUG", temp_childs_list);         // --> returns [Swallowtail, Small White, Large White, Silverfish]

    map_groups_childs.put(list_group_titles.get(k), temp_childs_list);

    Log.i("DEBUG", map_groups_childs.size());                 // --> returns 1 not 10
    Log.i("DEBUG", map_groups_childs.containsKey("insects")); // --> returns true
    Log.i("DEBUG", map_groups_childs.get("insects").size());  // --> returns 4 not 0

Comments

2

Even with the edit, your code is still missing huge clues about what is going on. I've made some guesses and if I'm right, you are making this way too hard.

I've inferred the existence of a class Id_triv_cat that has getters named get_name() and get_type().

static class Id_triv_cat {

    String type;
    String name;

    Id_triv_cat( String type, String name )
    {
        this.name = name;
        this.type = type;
    }

    public String get_type()
    {
        return type;
    }
    public String get_name()
    {
        return name;
    }
}

I've also written this code to test your prepareListData() method.

public static void main(String[] args){
    ArrayList<Id_triv_cat> search_results = new ArrayList<Id_triv_cat>();
    search_results.add( new Id_triv_cat("type1", "name1") );
    search_results.add( new Id_triv_cat("type2", "name2") );
    search_results.add( new Id_triv_cat("Insekten", "insekten name1") );
    search_results.add( new Id_triv_cat("Insekten", "insekten name2") );
    search_results.add( new Id_triv_cat("type3", "name3") );
    new Test().myPrepareListData( search_results );
}

And while I could fix your minor defect like a good SE denizen I'm going to risk having all of this migrated to Programmers because your biggest problem is a design problem. Your code is suffering from a lack of clear local identifiers. Instead of making locals you are having a dot fest with the java utility classes that's making your code pointlessly hard to follow.

If, as I suspect, you are trying to build a map from a list of Id_triv_cat's using their type as a key and name as a value then try this:

private void myPrepareListData(ArrayList<Id_triv_cat> search_results) {
    // Each group title is mapped to a list of its respective children
    HashMap<String, ArrayList<String>> my_map_groups_childs = 
        new HashMap<String, ArrayList<String>>(); 

    for (Id_triv_cat search_result : search_results)
    {
        String type = search_result.get_type();
        String name = search_result.get_name();

        if ( my_map_groups_childs.containsKey(type) )
        {
            ArrayList<String> names = my_map_groups_childs.get(type);
            names.add(name);
        }
        else
        {
            ArrayList<String> names = new ArrayList<String>();
            names.add(name);
            my_map_groups_childs.put(type, names);
        }
    }

    Log.i("DEBUG", "my_map_groups_childs = " + my_map_groups_childs);
}

This displays: my_map_groups_childs = {Insekten=[insekten name1, insekten name2], type1=[name1], type3=[name3], type2=[name2]}

See how a few well chosen local's can make life so much easier?

If that's not what you wanted you're going to have to make your question clearer.

And Radiodef is right. You really should use camelCase when you code in Java.

2 Comments

Your approach is indeed much simpler if it does the same as mine. Usually I tend to avoid for-each-loops, as I find them hard to read, but I'll give it a try and try to understand your code.
For each is a wonderful abstraction if used correctly. However, I also like to use indexes. But I've learned to hold back and now use them for times when I have a real need to get my hands on the index. The code I presented here would only need one more line if I converted to using an index. Really, it's the locals (type, name, names) that make this so much easier to read.

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.