10

I've run into the same problem a lot of people seem to face coming from PHP, the lack of a decent and easy to use associative array solution. I've read quesions here which basically all suggested to use a HashMap, like this Q: Java associative-array

However, I don't think the solutions mentioned will solve my problem. I'll explain.

I have a list with 250 items (countries) for which I want to store data. The data is of undefined length, meaning it can hold multiple entries per "column", sometimes no entry, sometimes 4, etcetera.

in PHP I could just do this:

$country_data = new array();
$country_data["nl"]["currency"] = "euro";
$country_data["nl"]["languages"] = "Dutch";
...
$country_data["us"]["currency"] = "US dollar";
$country_data["us"]["languages"] = array("English", "Spanish");

So sometimes I want to store an array, sometimes not. Ofcourse it could also be a array with only one entry instead of a string, but I'm just saying.

So, the question is, how do I store and fetch arrays in arrays in a HashMap? I understand I am pretty much stuck with the ugly HashMap solution, but I still can't see how this will let me store arrays, I'm sure I'm overlooking something simple. An example based on mine would be great!

UPDATE

I opted to go for HashMaps of HashMaps The reason for this I need to be able to oversee everything easily, and change a few lines of values when needed. And this is flexible, I can easily just get a country name based on country code, a language, or I can get the country_data HashMap when I need it, or all country names, etcetera.

public class iso_countries {  
    Map<String, Object> country_data        = new HashMap<String, Object>();
    Map<String, String> country_name        = new HashMap<String, String>();
    Map<String, String[]> country_idd       = new HashMap<String, String[]>();
    Map<String, String[]> country_cid       = new HashMap<String, String[]>();

    public iso_countries(){      
         country_name.put("nl",         "Netherlands");      
         country_idd.put("nl",      new String[]{"+31"});
         country_cid.put("nl",      new String[]{"#31#", "*31#"});
         setData(country_name, country_cid, country_idd);
         // 249 * 4 lines more and later...
    }//end method

    public void setData(Map country_name, Map country_cid, Map country_idd){
         country_data.put("name",   country_name);
         country_data.put("idd",    country_idd);
         country_data.put("cid",    country_cid);
    }//end method   

    public String getCountryName(String countryCode){
        String name     = country_name.get(countryCode);
        return name;
    }//end method

    public String[] getCountryIdd(String countryCode){
        String prefix[] = country_idd.get(countryCode);
        return prefix;
    }//end method

    public String[] getCountryCid(String countryCode){
        String cid[]    = country_cid.get(countryCode);
        return cid;
    }//end method
}//end class
Community
  • 1
  • 1
slinden77
  • 3,378
  • 2
  • 37
  • 35
  • You want hashmap like behavior in php or will you be implementing the code in java? – Hari Menon Jun 01 '12 at 19:45
  • 3
    Why do your lists have country codes AND indexes? Doesn't `$country_data["us"]` work perfectly fine? – Brendan Long Jun 01 '12 at 20:11
  • 1
    just to comment on your PHP code: it doesn't even make any sense, that the value types in the `'language'` index can be totally different, once a string, once an array... you should decide which you prefer, and stick to it. Mixing types indicates a really bad coding practice, it makes your code erratic. EDIT: @Brendan Long: yes, that's a good notice too. – Sk8erPeter Jun 01 '12 at 20:14
  • Java isn't the same language as PHP, and you shouldn't use the same techniques. Here, maps and lists of "country" objects with strongly typed fields would be a much more appropriate solution. – Louis Wasserman Jun 01 '12 at 21:35
  • @BrendanLong yes, the numeric index is indeed not needed. I am currently reading all the answers, still learning Java, so I struggling a bit to even grasp some of the answers! Thanks everyone, I will investigate further and mark the appropriate Q with answered and post my solution for future reference. – slinden77 Jun 01 '12 at 22:01
  • @Sk8erPeter you are right, but if one knows how it's coded, one can always determine the type and do something accordingly. It all depends on the context of the application and where it's used. – slinden77 Jun 01 '12 at 22:07
  • @dmmh: I think you shouldn't make your life harder with stuff like that. If there can be multiple values, and you should pass the appropriate ones in an array, than use an array, and never mix the types with e.g. strings. If there is just one value, OK then, there will be an array with only one item. But the code stays consistent. – Sk8erPeter Jun 02 '12 at 01:37

6 Answers6

12

Maybe I'm misunderstanding your issue, but why not create your own object to hold the data, then store those in the HashMap?

Java is an object-oriented language, so why not use its most famous facility: objects!

kevin628
  • 3,458
  • 3
  • 30
  • 44
  • 5
    Maybe because after years of Java experience you may learn PHP and find out that associative arrays are the objects you have ever searched for. They're a lot cooler, because you just fill them right out of a database, and access them right in your GUI. No Java data models needed, so 20% less work at least. You change your hole database? No problem, your old Java GUI still runs if it uses associative arrays. You just need to fill the same arrays from the new database. In the end it's about creating arrays of hash maps instead of hash maps of objects like you proposed. Programming is an art. ;) – Marcus Feb 05 '13 at 00:23
  • 1
    yes, it's just a whole lot more work to achieve something so simple – slinden77 Apr 16 '13 at 19:14
  • @Marcus that's a good observation. Hadn't thought of that before. – kevin628 May 24 '13 at 16:58
11

An array in PHP is actually a hash, not an array.

The correct thing to use in java is a HashMap. You can also store multpile values in a Hashmap by storing arrays or lists in it. So HashMap<String, HashMap<String, Object> or Hashmap<String, List<Object>> might help you.

When you use multi-dimensional arrays, you have to use integers as key.

Sam Dufel
  • 17,560
  • 3
  • 48
  • 51
Polygnome
  • 7,639
  • 2
  • 37
  • 57
8

You can store arrays as values of a HashMap:

HashMap<Integer,String[]> hm = new HashMap<Integer,String[]>();
hm.put( 1, new String[]{ "a", "b" } );

As for as having "multi-dimensional" keys, you can always wrap them together with a class. Another, albeit ugly, solution would be to have HashMaps of HashMaps.

tskuzzy
  • 35,812
  • 14
  • 73
  • 140
  • 1
    If your keys are consecutive integers, why not use an array or a list rather than a map? – Jonathon Faust Jun 01 '12 at 19:58
  • thanks, I chose this as my solution. I actually opted for the ugly solution and am using HashMaps of HashMaps. I will post my code later and explain. – slinden77 Jun 09 '12 at 18:04
5

The correct way is to use a Country object:

class Country {
    // Might want these private with getters and/or setters
    public String currency;
    public Set<String> languages;

    // String...languages is like String[] languages, except you can pass the
    // arguments in like new Country("Euro", "Dutch", "German")
    // instead of new Country("Euro", new String[] { "Dutch", "German" })
    // It's purely stylistic
    public Country(String currency, String... languages) {
        this.currency = currency;
        this.languages = new HashSet<String>();
        for(String language : languages) {
            this.languages.add(language);
        }
        // or this.languages = new HashSet<String>(Arrays.asList(languages));
    }
}

void someFunction() {
    Map<String, Country> countries = new HashMap<String, Country>():
    countries.put("us", new Country("US Dollar", "English", "Spanish"));
    countries.put("nl", new Country("Euro", "Dutch"));
}

You could do this by nesting Lists and Maps, by why use Java if you're not going to use the tools the compiler gives you?

Brendan Long
  • 53,280
  • 21
  • 146
  • 188
  • 1
    This is the right idea. I'd suggest that the `languages` member field be either `Set` or `List` rather than an array. In the constructor, one could either iterate through the languages arg to add them to the set one at a time or else use `Arrays.toList()`. – Ted Hopp Jun 01 '12 at 19:59
2
Map<String, Object> map = new HashMap<String, Object>();

now instead of Array you place List, in normal cases you will just place String as the value.

Another approach will be to use value object which stores whatever you have give it to.

public class ValueObject {

   private Map<String, List<Object>> dataHolder = null; //getter, setter
}

Map<String, ValueObject> map = new HashMap<String, ValueObject>();
mprabhat
  • 20,107
  • 7
  • 46
  • 63
1

Alternatively, if your information about countries, currencies and languages can be statically defined, you could put all that in an set of enums:

public enum Language {
    EN, ES, FR; 
}

public enum Currency    {
    USD, GBP, CAD;
}

public enum Country {

    US(USD, EN), UK(GBP, EN), CAN(CAD, EN, FR);

    private final Language[] languages;
    private final Currency currency;

    Country(Currency currency, Language... languages) {
        this.currency = currency;
        this.languages = Arrays.copyOf(languages, languages.length);
    }

    public Currency getCurrency() {
        return this.currency;
    }

    public Collection<Language> getLanguages() {
        return asList(this.languages);
    }
}

Then you could very easily do something like:

Map<Integer, Country> data = new HashMap<Integer, Country>();
data.put(0, US);
data.put(1, UK);

Currency currency = data.get(0).getCurrency();
Collection<Language> langs = data.get(0).getLanguages();

System.out.println(currency);
System.out.println(langs);
Edwin Dalorzo
  • 76,803
  • 25
  • 144
  • 205