Java equivalent of Perl's hash
I've been using a lot Perl hashes due to super flexibility and convenient. for instance, in Perl I can do the following:
$hash{AREA_CODE}->{PHONE}->{STREET_ADDR}
I wondering how can I accomplish the same thing with Java, I guess it has something to do with HashMap?
开发者_运维问答Thanks,
I've been using a lot Perl hashes due to super flexibility and convenient. for instance, in Perl I can do the following:
$hash{AREA_CODE}->{PHONE}->{STREET_ADDR}
I wondering how can I accomplish the same thing with Java, I guess it has something to do with HashMap?
The Java code which approximates the following Perl code:
my %hash;
$hash{AREA_CODE}{PHONE}{STREET_ADDR} = "221B Baker Street";
printf "Street address is %s\n", $hash{AREA_CODE}{PHONE}{STREET_ADDR};
is
HashMap<String, HashMap<String, HashMap<String, String>>> hash =
new HashMap<String, HashMap<String, HashMap<String, String>>>();
hash.put("AREA_CODE", new HashMap<String, HashMap<String, String>>());
hash.get("AREA_CODE").put("PHONE", new HashMap<String, String>());
hash.get("AREA_CODE").get("PHONE").put("STREET_ADDR", "221B Baker Street");
System.out.printf("Street address is %s\n",
hash.get("AREA_CODE").get("PHONE").get("STREET_ADDR"));
Isn’t that special? :)
I say ‘approximates’ for many reasons. One of these is that in Java you’ll be frustrated to the point of extreme apoplexy merely for wanting to then do on the next line of Java the equivalent of this perfectly straightforward Perl code:
$hash{AREA_CODE}{PREFIX} = 800;
If you want Perl’s flexibility and convenience in things like this, Java simply isn’t going to give it to you. Even worse, its partisans will often berate you for even expressing such a desire.
First of all, your specific example ($hash{AREA_CODE}->{PHONE}->{STREET_ADDR}
), with hard-coded strings as hash keys, is not really a useful data structure in Java as Michael Carman pointed out - it should be stored as a class with attributes (and to be honest it's a bad data structure in concept - data like this is more likely to be an array of phones, not hash of phones).
Second, assuming you actually meant $hash{$AREA_CODE}->{$PHONE}->{$STREET_ADDR}
, it looks like everyone's Java code so far was NOT implementing a generic equivalent code - the code all assumed that the Java hash is newly initialized for storing example OR fully populated for retrieval example (in other words, as leonbloy's answer noted, is missing autovivification feature).
The correct code mimiquing autovivification is:
// This method will ensure that hash-of-hash-of-hashes structure exists of a given set of 3 keys.
public HashMap<String, HashMap<String, HashMap<String, Object>>>
autovivification_3rd_level (
HashMap<String, HashMap<String, HashMap<String, Object>>> hash
, String AREA_CODE, String PHONE, String STREET_ADDR) {
if (hash == null) {
hash = new HashMap<String, HashMap<String, HashMap<String, Object>>>();
}
if (!hash.contains(AREA_CODE) || hash.get(AREA_CODE) == null) {
hash.put(new HashMap<String, HashMap<String, Object>>());
}
HashMap<String, HashMap<String, Object>> AREA_CODE_hash
= (HashMap<String, HashMap<String, Object>>) hash.get(AREA_CODE);
if (!AREA_CODE_hash.contains(PHONE) || AREA_CODE_hash.get(PHONE) == null) {
AREA_CODE_hash.put(new HashMap<String, Object>());
}
return hash;
}
////////////////////////////////////////////////////////////////////////////////////
// Equivalent to Perl's "$hash{$AREA_CODE}->{$PHONE}->{$STREET_ADDR} = value;"
public Object put_3d_level_hash(
HashMap<String, HashMap<String, HashMap<String, Object>>> hash
, String AREA_CODE, String PHONE, String STREET_ADDR,
, Object value) {
hash = autovivification_3rd_level(hash, AREA_CODE, PHONE, STREET_ADDR);
return hash.get(AREA_CODE).get(PHONE).put(STREET_ADDR, value);
}
put_3d_level_hash(hash, AREA_CODE, PHONE, STREET_ADDR, obj);
////////////////////////////////////////////////////////////////////////////////////
// Equivalent to Perl's "$var = $hash{$AREA_CODE}->{$PHONE}->{$STREET_ADDR}"
public Object get_3d_level_hash(HashMap<String, HashMap<String, HashMap<String, Object>>> hash
, String AREA_CODE, String PHONE, String STREET_ADDR) {
hash = autovivification_3rd_level(hash, AREA_CODE, PHONE, STREET_ADDR);
return hash.get(AREA_CODE).get(PHONE).get(STREET_ADDR);
}
Object obj = get_3d_level_hash(hash, AREA_CODE, PHONE, STREET_ADDR);
See the Map interface and its implementations, specially HashMap.
Beware that Java doesn't have Perl's auto-vivification (handy but dangerous feature) so that
hash.get("areaCode").get("phone").get("streetAdr")
will throw an exception if, eg, get(phone) returns null. Beware also that you should not uses hashes for things that have fixed names ("properties"), you should define your own classes with its getters and setters.
Java has hashes, but because of strong typing they're not quite as flexible as hashes in Perl. Multidimensional hashes are harder to work with. In Perl, you can just declare a hash and let autovivification create the nested hashes on demand.
my %hash;
$hash{a}{b} = 1;
In Java, you have to declare it to be a hash-of-hashes up-front.
Map<String,Map<String,Integer>> hash = new HashMap<String,HashMap<String,Integer>>();
hash.put("a", new HashMap<String, Integer>());
hash.get("a").put("b", new Integer(1));
For every extra dimension you need to add another nesting of Map<K,V>
to the declaration. Aside from being tedious, this isn't very OO.
If the hash keys are constant, why won't hash.getAreaCode().getPhone().getStreetAddr()
do? Keep in mind that either your getters or your constructors will need to handle default value generation.
You can easily subclass your hash to add a method that'll autovivify for you.
From: $hash{AREA_CODE}->{PHONE}->{STREET_ADDR}
To: hash.vivifyingGet(areaCode).put(phone, streetAddr)
.
Assuming I've created the hash with:
/**
* A two-level autovivifying hashmap of X and Y to Z. Provides
* a new method #vivifyingGet(X) which creates the next level of hash.
*/
Map<AreaCode, Map<Phone, StreetAddr>> hash =
new HashMap<AreaCode, Map<Phone, StreetAddr>>() {
/**
* Convenience method to get or create the next level of hash.
* @param key the first level key
* @return the next level map
*/
public Map<Phone, StreetAddr> vivifyingGet(Phone key) {
if (containsKey(key)) {
return get(key);
} else {
Map<Phone, StreetAddr> = hash = new HashMap<Phone, StreetAddr>();
put(key, hash);
return hash;
}
}
};
I missed the perl hashes a lot in my work and made some ugly workarounds with hash classes.
Last week I had an idea to implement the whole thing in one PerlMap
class which use delimiters to access objects and foremost the Lists
zu access subsets.
It works fine with map.get(code:street:phone)
and map.put(code:street:phone,"123456789")
. To get a list of phonenumber you just use map.getList(code:street)
.
I've just started but use in my project now. It has no limitations of complexity :-) and you can choose the delimiter free. I put the whole stuff under http://www.jdeer.org. Have fun.
You're probably going to want to go with Groovy if you want this sort of flexibility but still run within the JVM. tchrist likes to ignore the point that Java is strong-typed as opposed to dynamic-typed languages like Perl or PHP - and also likes to ignore that Java is an order of magnitude faster at running, but that's just me being a "partisan", apparently.
精彩评论