Home | Contact Us | FAQ | Search & Site Map | Link to Us
Sign In | Join | Other 45 Sites in Network
HomeAnnouncementsWhite Papers
Discussion GroupsFirst AidDatabasesJavaBeansGUIJava 3DVirtual MachineCORBASecurityToolsGeneral
Java DirectoryOpen Source ProjectsSample Book ChaptersUser GroupsWeb Resources
Related Topics
Databases.NETMore Topics ...

Java Forum / General / July 2006

Tip: Looking for answers? Try searching our database.

Multidimensional Associative Arrays..

Thread view: 
lordy - 18 Jul 2006 23:46 GMT
Any nice way to do the Java equivalent of the perl..

$myHash{val1}{val2}{val3} = val4

Eg. myHash.get(val1) is another Hashtable 'hash2'.
hash2.get(val2) is another Hashtable 'hash3' etc. etc.

At each step, if the intermediate hash doesn't exist it is created.

I've rolled my own method that takes an array of objects and creates the
required nested Maps as it steps through the array. Just wondered if I'm
re-inventing a wheel..

Cheers,
Lordy
Stefan Ram - 19 Jul 2006 00:10 GMT
>Any nice way to do the Java equivalent of the perl..
>$myHash{val1}{val2}{val3} = val4
>At each step, if the intermediate hash doesn't exist it is created.

 This is called »autovivification«.

Here is a simple example of autovivification in a special
 case for Java:

class NumericMapUtils
{ public static <D> void addTo
 ( final java.util.Map<D,java.lang.Integer> map, final D d, final int i )
 { map.put( d, i +( map.containsKey( d )? map.get( d ): 0 )); }}

 Now one can declare a map:

final java.util.Map<java.lang.Integer,java.lang.Integer> map =
new java.util.HashMap<java.lang.Integer,java.lang.Integer>();

 And then add 7 to its entry 4:

NumericMapUtils.<java.lang.Integer>addTo( map, 4, 7 );

 If the entry 4 does not exist yet, it will be created (hence,
 "autovivification") with an initial value of 0 by »addTo«.

 Your case from above could use an interface like (simplified):

setHash
( final java.util.Map map, final java.lang.Object newValue,
 final java.lang.Object keys ... )

 And for your example would be called like

setHash( myHash, val4, va11, val2, val3 );

 In Perl, I dynamically build an expression like
 »::root->{a}->{b}->...->{z}« and then use »eval« to build the
 whole path including all hashes required by autovivification,
 where the number of hashes required is only known at runtime.

 Eventually »$source« is stored at the new location within
 this map of maps of maps of ... maps. All it needs is:

my $buffer = '';
while( $psource =~ /(:|\/|[^:\/]+)/g ){ $buffer .= "->{'" . $1 . "'}"; }
my $expression = '\$::root' . $buffer;
my $place = eval( $expression ); ${$place}->{'#'}= $source;

 Without »=~«, without »eval« and without autovivification, it
 needs some more than four lines of Java to express the same
 thing in Java.

dimitar <dimitar@example.com> writes:
>FWIW one of the idioms for doing the 'autovivification' thing in Java is:
>synchronized(masterMap) {
[quoted text clipped - 3 lines]
>it takes six lines, and it's thread-safe. Anything less and you might
>loose data if more than one threads try to access the structure.

 It is still not quite the same, because it contains the fixed
 assumption "new HashMap()", while in Perl this also could be
 "new List()" or "new Integer()" depending on the client code
 used.

 If that would be possible in Java, one would be able to write

mainMap.get( key ).put( subKey, value );

 and get an autovivificated Map (as above), but also

mainMap.get( key ).add( value )

 and get an autovivificated List or

mainMap.get( key )+= 12

 and get an autovivificated Integer.

 Here's the complete Perl script:

use Data::Dumper;
$names->{ "branch" }->{ "account" } = 123;
$names->{ "branch1" }->[ 3 ] = 123;
$names->{ "branch2" } += 12;
print Data::Dumper::Dumper( $names ), "\n";

 and its output is:

$VAR1 = {
         'branch' => {
                       'account' => 123
                     },
         'branch1' => [
                        undef,
                        undef,
                        undef,
                        123
                      ],
         'branch2' => 12
       };

 Without autovivification, in Java, one needs some work of the
 programmer, for example, to create a nested array with a
 dimension given at runtime:

 For example, the following code builds an array with three
 dimensions of the extension 4, 5, and 6, respectively.
 The vivification happens recursively in »build« using
 »java.lang.reflect.Array.newInstance«. The number of arguments
 of »build« gives the dimension.

 A call to newInstance alone with the extensions will give
 an array object with the correct type, but its entries will
 still be empty and not other array objects. So, the recursion
 of »build« is needed to fill all array entries with other
 subarrays up to the lowest level.

public class Main
{
 public static int[] cdr( final int[] list )
 { return java.util.Arrays.copyOfRange( list, 1, list.length ); }

 public static java.lang.Object build( final int ... extensions )
 { final java.lang.Object array = java.lang.reflect.Array.newInstance
   ( java.lang.Integer.TYPE, extensions );
   for( int i = 0; i < extensions[ 0 ]; ++i )if( extensions.length > 1 )
   java.lang.reflect.Array.set( array, i, build( cdr( extensions )));
   else java.lang.reflect.Array.setInt(( int[] )array, i, i );
   return array; }

 public static void print( final java.lang.Object array )
 { for( int i = 0; i < java.lang.reflect.Array.getLength( array ); ++i )
   if( array.getClass().getName().startsWith( "[[" ))
   print( java.lang.reflect.Array.get( array, i )); else
   java.lang.System.out.print( java.lang.reflect.Array.getInt( array, i )); }

 public static void main( final java.lang.String[] args )
 { print( build( 4, 5, 6 )); }}
lordy - 19 Jul 2006 00:57 GMT
>>Any nice way to do the Java equivalent of the perl..
>>$myHash{val1}{val2}{val3} = val4
[quoted text clipped - 4 lines]
>  Here is a simple example of autovivification in a special
>   case for Java:

Thanks a lot. Plenty to chew on ..

Lordy
Ingo R. Homann - 19 Jul 2006 09:25 GMT
Hi lordy,

> Any nice way to do the Java equivalent of the perl..
>
> $myHash{val1}{val2}{val3} = val4
>
> Eg. myHash.get(val1) is another Hashtable 'hash2'.
> hash2.get(val2) is another Hashtable 'hash3' etc. etc.

IMHO there are (at least) two ways to achieve that. The first one is...

> At each step, if the intermediate hash doesn't exist it is created.

...Stefan showed a way to do this e.g. by a static helper-method (I did
not read hist posting til the end, because I (and many other people)
think that his code is unreadable - but thanks god there are some good
code-formatters :-)

My way would be to implement a self-verifying Map by encapsulating (and
delegating to) a java.util.HashMap.

Another - totally different (and perhaps easier, but not so
flexible) - possibilty would be to combine the three values to a new key:

String val1=..., val2=..., val3=...;

// some char that does not occur in the Strings above:
String sepChar="|";

String key=val1+sepChar+val2+sepChar+val3;

Map<String,Something> map=...
map.get(key);

If you want to do it a bit 'cleaner', you can wrap the vals into a new
class:

class MyKey {
  String val1, val2, val3;
  int hashCode() {
   return val1.hashCode()+val2.hashCode()+val3.hashCode();
  }
  boolean equals(Object o) {
    MyKey k=(MyKey)o;
    return val1.equals(k.val1)&&val2.equals(k.val2)&&val3.equals(k.val3);
  }
}

Hth,
Ingo
Piotr Kobzda - 20 Jul 2006 13:06 GMT
> Any nice way to do the Java equivalent of the perl..
>
[quoted text clipped - 4 lines]
>
> At each step, if the intermediate hash doesn't exist it is created.

Try the following:

<code>
import java.util.HashMap;
import java.util.Map;

public class AutoMapValue<K, V> {

   private Map<K, AutoMapValue<K, V>> map;
   private V value;

   public Map<K, AutoMapValue<K, V>> getMap() {
      if (map == null)
         map = new HashMap<K, AutoMapValue<K, V>>();
      return map;
   }

   public AutoMapValue<K, V> get(K key) {
      Map<K, AutoMapValue<K, V>> map = getMap();
      AutoMapValue<K, V> e = map.get(key);
      if (e == null)
         map.put(key, e = new AutoMapValue<K, V>());
      return e;
   }

   public AutoMapValue<K, V> get(K... keys) {
      AutoMapValue<K, V> e = this;
      for(K k : keys)
         e = e.get(k);
      return e;
   }

   public V get() {
      return value;
   }

   public void set(V value) {
      this.value = value;
   }

   public String toString() {
      return map + "|" + value;
   }

}
</code>

Sample usage:

AutoMapValue<String, Integer> amv = new AutoMapValue<String, Integer>();

amv.get("a").set(1);
amv.get("a", "b", "c").set(3);

amv.get("a").get("b").get();  // null
amv.get("a").get("b").get("c").get();  // 3

piotr


Free Magazines

Get these publications absolutely FREE for up to 12 months. There are no hidden fees and no obligation. Simply choose a title, complete the application form and submit it. Read more ...

Oracle MagazineNetwork ComputingComputer WorldBio-IT WorldeWeekInformation WeekInfosecurity
 
Sign In
Join
My Latest Posts
My Monitored Threads
My Blog
My Photo Gallery
My Profile
My Homepage

Start New Thread
Enable EMail Alerts
Rate this Thread



©2008 Advenet LLC   Privacy Policy - Terms of Use
This website includes both content owned or controlled by Advenet as well as content owned or controlled by third parties.