2

First of all I apologize if this question is inappropriate.

I am implementing a country picker feature in my Android application and I found a github project and it is a good start for me. The code works just fine and as expected.

I started by breaking it down to understand how it works.

This is the blurry part of code

@Override
    protected ArrayList<Country> doInBackground(Void... params) {
        ArrayList<Country> data = new ArrayList<Country>(233);
        BufferedReader reader = null;
        try {
            reader = new BufferedReader(new InputStreamReader(mContext.getApplicationContext().getAssets().open("countries.dat"), "UTF-8"));

            // do reading, usually loop until end of file reading
            String line;
            int i = 0;
            while ((line = reader.readLine()) != null) {
                //process line
                Country c = new Country(mContext, line, i);
                data.add(c);
                ArrayList<Country> list = mCountriesMap.get(c.getCountryCode());
                if (list == null) {
                    list = new ArrayList<Country>();
                    mCountriesMap.put(c.getCountryCode(), list);
                }
                list.add(c);
                i++;
            }
        } catch (IOException e) {
            //log the exception
        } finally {
            if (reader != null) {
                try {
                    reader.close();
                } catch (IOException e) {
                    //log the exception
                }
            }
        }
        //this code enables app to pick the right default country from the spinner
        String countryRegion = PhoneUtils.getCountryRegionFromPhone(mContext);
        int code = mPhoneNumberUtil.getCountryCodeForRegion(countryRegion);

        final ArrayList<Country> list = mCountriesMap.get(code);

        /*getActivity().runOnUiThread(new Runnable() {
            @Override
            public void run() {
                for(int i = 0; i < list.size(); i++)
                    Toast.makeText(mContext, "ds: " + list.get(i).getName(), Toast.LENGTH_SHORT).show();
            }
        });*/
        if (list != null) {
            for (Country c : list) {
                if (c.getPriority() == 0 || c.getPriority() == 1) {
                    mSpinnerPosition = c.getNum();
                    break;
                }
            }
        }
        return data;
    }

This code reads a file in the asset folder and add objects to a list. After that it picks the country of the device and selects it from the spinner of all countries

In the while loop, I think this line list.add(c); makes no sense because the while loop will loop again and new list object is created. If I comment this line, mCountriesMap.get(code) returns empty list even though mCountriesMap isn't null.

So my question is why this behavior is happening? What list.add(c); is doing here ?

Ryan M
  • 18,333
  • 31
  • 67
  • 74
Amine
  • 2,241
  • 2
  • 19
  • 41
  • List is getting data from 'value' of hashmap. If there is no key present in the hashmap, then put operation is performed. – Ashish John Dec 03 '19 at 07:44
  • Sorry if I didn't get you right, but I don't understand why we are putting the empty list to the map then we are filling the list – Amine Dec 03 '19 at 12:44
  • What I understood is there is a HashMap which saves only the first list corresponding to a country code. And each time taking only that first list from HashMap and adding Country to that list. – Sunil Sunny Dec 03 '19 at 22:46
  • It's just adding the list of countries that have same country code to a HashMap. – Amine Dec 03 '19 at 23:19

2 Answers2

2

Imagine there are 3 country codes {US,US,AU}. So running this will result in

mCountriesMap{
              US: List{US,US}
              AU: List{AU} 
             }

I think your doubt is list.add(c); is after mCountriesMap.put(c.getCountryCode(), list);

I will try to explain step by step

First case

ArrayList<Country> list = mCountriesMap.get(US); //No value in mCountriesMap
if (list == null) { //list is null so true
    list = new ArrayList<Country>();
    mCountriesMap.put(US, list); //mCountriesMap{US: List{} }          
}
list.add(US); //mCountriesMap{US: List{US} }  here hashmap will get updated

Second case

ArrayList<Country> list = mCountriesMap.get(US); //get the prevous list
if (list == null) { //list have value so false
    list = new ArrayList<Country>();
    mCountriesMap.put(US, list); //mCountriesMap{US: List{US} }          
}
list.add(US); //mCountriesMap{US: List{US,US} }  here hashmap will get updated

And the 3rd one will be just a repeat of the first case.

Sunil Sunny
  • 3,949
  • 4
  • 23
  • 53
  • Now it becomes clear to me, but how can you explain the HashMap updating when we add to the list? I mean what this concept is called? – Amine Dec 04 '19 at 04:33
  • Also can you please tell me why if i changed the place of this statement `list.add(US);` inside the the `if` statement , it doesn't work? taking into consideration that list is always null to prevent adding the same list twice. – Amine Dec 04 '19 at 04:37
  • I think for your first question @RyanMentley have already given you a referral link in the comment section. For the 2nd question, it won't work since the `list` is not `null` and the if condition is `false` so the 2nd country won't get updated in the hashmap. – Sunil Sunny Dec 04 '19 at 04:43
  • Also, what you said will work if there are no repeating country codes. – Sunil Sunny Dec 04 '19 at 04:50
  • 1
    Thank you for your explanation. – Amine Dec 04 '19 at 16:12
1

The important part to consider is

mCountriesMap.put(c.getCountryCode(), list);

This adds the newly created list to mCountriesMap, so even though it creates a new list each iteration through the loop, the previously created lists are still accessible through the map.

Ryan M
  • 18,333
  • 31
  • 67
  • 74
  • But `list.add(c);` is after `mCountriesMap.put(c.getCountryCode(), list);` , so how is that done ? – Amine Dec 03 '19 at 05:23
  • 2
    The list that's in the `list` variable and the one that's put into the map are the same list, so adding it to one adds it to the other. – Ryan M Dec 03 '19 at 10:04
  • So if I can understand you right, even if I fill the list after putting it's value to the map,the content of the list will pe present in the map for that specific key in that specific iteration? Because here is how I understood the functionality of that code:`list = null -> map.put("usa", null) ->list = {"usa"} -> list =null -> ....` – Amine Dec 03 '19 at 12:52
  • 1
    Think of it in terms of the object (the `ArrayList`), not the variable (`list`). A new `ArrayList` might be stored in the `list` variable in a later iteration, but within an iteration, the one in `list` and the one in the map are the same. You're storing the `ArrayList` object in the map - the fact that it came from `list` doesn't matter. – Ryan M Dec 03 '19 at 23:06
  • https://stackoverflow.com/a/40523/208273 has some additional information on how this works in Java that you may find helpful – Ryan M Dec 03 '19 at 23:07
  • So in the second iteration for example, the list will still have the country that has been added to it the first iteration because `ArrayList list = mCountriesMap.get(c.getCountryCode());` is passed by value not reference? – Amine Dec 03 '19 at 23:21
  • I also tried to execute this statement `list.add(c)` before putting the list to the HashMap and it didn't work. – Amine Dec 03 '19 at 23:22