关联数组和Java

10

我遇到了许多人似乎都会面临的问题,即缺乏一个好用且易于使用的关联数组解决方案。我在这里看到了一些问题,基本上都建议使用HashMap,例如这个问题:Java associative-array

然而,我认为提到的解决方案都不能解决我的问题。我来解释一下。

我有一个包含250个项目(国家)的列表,我想要存储数据。数据长度未定义,即可以每个“列”容纳多个条目,有时没有条目,有时有4个等等。

在PHP中,我只需这样做:

$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");
有时我想存储一个数组,有时则不需要。当然也可以是只有一个条目的数组而不是字符串,但我只是这么说。
那么问题来了,如何在HashMap中存储和提取数组中的数组?我知道我几乎被困在丑陋的HashMap解决方案中,但我仍然看不到如何让我存储数组,我肯定是忽略了一些简单的东西。一个基于我的示例的示例将非常棒!
更新
我选择使用HashMaps of HashMaps。 原因是我需要能够轻松地监控所有内容,并在需要更改几行值时进行更改。而且这很灵活,我可以根据国家代码获取国家名称、语言,或者在需要时获取country_data HashMap,或者所有国家名称等。
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

你想在PHP中实现类似哈希映射的行为,还是要在Java中实现代码? - Hari Menon
3
你的列表为什么要有国家代码和索引?$country_data["us"]不是完全可以正常工作吗? - Brendan Long
1
只是对你的PHP代码进行评论:它甚至没有任何意义,'language'索引中的值类型可以完全不同,有时是字符串,有时是数组...你应该决定你更喜欢哪种类型,并坚持使用它。混合类型表明了一种非常糟糕的编码实践,它会使你的代码变得不稳定。编辑:@Brendan Long:是的,这也是一个很好的注意点。 - Sk8erPeter
@Sk8erPeter 你说得没错,但如果一个人知道它是如何编码的,他就可以确定类型并相应地做一些事情。这完全取决于应用程序的上下文和使用位置。 - slinden77
@dmmh:我认为您不应该用这样的东西使您的生活更加困难。如果有多个值,并且应该在数组中传递相应的值,则使用数组,永远不要混合类型,例如字符串。如果只有一个值,那么好吧,将有一个只有一个项的数组。但代码保持一致。 - Sk8erPeter
显示剩余2条评论
6个回答

12

也许我误解了你的问题,但为什么不创建自己的对象来保存数据,然后将它们存储在HashMap中呢?

Java是一种面向对象的语言,所以为什么不使用其最著名的工具:对象!


5
也许是因为在多年的Java经验之后,你学习了PHP,并发现关联数组正是你一直在寻找的对象。它们更加酷炫,因为你可以直接从数据库中获取数据并在GUI中访问它们。不需要Java数据模型,因此至少可以减少20%的工作量。如果你改变了整个数据库怎么办?没问题,只要使用关联数组的旧Java GUI仍然可以运行。你只需要从新数据库中填充相同的数组即可。最终,这就是创建哈希映射数组而不是对象哈希映射的数组,就像你所建议的那样。编程是一门艺术。;) - Marcus
1
是的,为了实现这么简单的东西需要更多的工作量。 - slinden77
@Marcus 这是一个好的观察。以前没有想过。 - kevin628

11

在PHP中,数组实际上是哈希而不是数组。

在Java中正确使用的是HashMap。您也可以通过将数组或列表存储在其中来存储多个值。因此HashMap<String, HashMap<String, Object>Hashmap<String, List<Object>> 可能会对您有所帮助。

当您使用多维数组时,必须使用整数作为键。


8
您可以将数组存储为 HashMap 的值:
HashMap<Integer,String[]> hm = new HashMap<Integer,String[]>();
hm.put( 1, new String[]{ "a", "b" } );

对于具有“多维”键的情况,您可以始终将它们与类一起包装。另一种虽然丑陋但可行的解决方案是使用 HashMapHashMap


1
如果你的键是连续的整数,为什么不使用数组或列表而不是映射呢? - Jonathon Faust
谢谢,我选择了这个作为我的解决方案。实际上,我选择了一个不太美观的解决方案,并且正在使用HashMap的HashMap。稍后我会发布我的代码并进行解释。 - slinden77

5
正确的方法是使用一个Country对象:
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"));
}

你可以通过嵌套列表和映射来实现此操作,但是如果你不使用编译器提供的工具,为什么要使用Java呢?

1
这是正确的想法。我建议languages成员字段应该是Set<String>或者List<String>而不是数组。在构造函数中,可以通过迭代languages参数逐个将它们添加到集合中,或者使用Arrays.toList() - Ted Hopp

2
Map<String, Object> map = new HashMap<String, Object>();

现在,您可以将List替换为Array,在正常情况下,您只需要将String作为值放置即可。

另一种方法是使用值对象,它可以存储您给定的任何内容。

public class ValueObject {

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

Map<String, ValueObject> map = new HashMap<String, ValueObject>();

1

或者,如果您的有关国家、货币和语言的信息可以静态定义,您可以将所有这些信息放在一组枚举中:

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);
    }
}

那么你可以很容易地做出这样的事情:

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);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接