CSC/ECE 517 Summer 2008/wiki1 6 arraysandhashes: Difference between revisions

From Expertiza_Wiki
Jump to navigation Jump to search
 
(180 intermediate revisions by 2 users not shown)
Line 1: Line 1:
= Arrays and Hashes =
= Arrays and Hashes =


Arrays and hashes are built into Ruby. Arrays, of course, are built into Java too, but hashes are only available through library classes. Compare Ruby and Java arrays, as well as hashes. Write equivalent code sequences in the two languages that illustrate the convenience of programming these constructs in both languages.
In this article, we compare arrays and hashes in [http://www.ruby-lang.org/ Ruby] and [http://java.sun.com/ Java]. Arrays and hashes are built into Ruby. Arrays, of course, are built into Java too, but hashes are only available through library classes. We demonstrate using logically equivalent code sequences the convenience of programming in both languages. Our Ruby descriptions apply to version 1.8, and for Java, we utilize Java 6 to take advantage of the new constructs that eliminate the drudgery of manually boxing, unboxing, and declaring iterators.


= Arrays =
= Arrays =


While Java and Ruby both provide built-in support for arrays, they differ in the operations that can be performed on them. Arrays in both languages are objects, though this association is implicit in Java arrays, where arrays are [http://java.sun.com/docs/books/tutorial/java/nutsandbolts/arrays.html container objects] and whose [http://java.sun.com/docs/books/jls/third_edition/html/arrays.html#10.8 direct superclass is type Object]. In Ruby, arrays are easily immediately identifiable as objects and they are represented by the [http://www.ruby-doc.org/core/classes/Array.html Array] class.
While both Java and Ruby provide built-in support for arrays, they differ in the operations that can be performed on them. Arrays in both languages are objects, though this association is implicit in Java arrays, where arrays are [http://java.sun.com/docs/books/tutorial/java/nutsandbolts/arrays.html container objects] whose [http://java.sun.com/docs/books/jls/third_edition/html/arrays.html#10.8 direct superclass is type Object]. In Ruby, arrays are easily immediately identifiable as objects and they are represented by the [http://www.ruby-doc.org/core/classes/Array.html Array] class.


== Comparison of Common Operations in Ruby and Java ==
== Comparison of Common Operations in Ruby and Java ==


In this section, we provide a side-by-side comparison of common array operations in Ruby and compare and contrast them with Java.
In this section, we provide a side-by-side examination of common array operations in Ruby and compare and contrast them with Java.


=== Creating an Array ===
=== Creating an Array ===


One way to create an array in Ruby is to to use the new class method:
One way to create an array in Ruby is to use the <tt>new</tt> class method:


   planets = Array.new(8)
   planets = Array.new(8)


This creates an empty array named months. This is similar to the Java initialization:
This creates an empty array named <tt>planets</tt>. This is similar to the Java initialization:


   String[] planets = new String[8];
   String[] planets = new String[8];


Arrays can also be initialized to values in both Java and Ruby. For example, we can initialize an arrays of planets by entering the following in Java follows, noting the use of curly braces in array initialization:
Arrays can also be initialized to values in both Java and Ruby. For example, we can initialize the arrays of planets by entering the following in Java, noting the use of curly braces in array initialization:


   String[] planets = {"Mercury", "Venus", "Earth", "Mars", "Jupiter",  
   String[] planets = {"Mercury", "Venus", "Earth", "Mars", "Jupiter",  
         "Saturn", "Uranus", "Neptune" };
         "Saturn", "Uranus", "Neptune" };


Some people consider Pluto a planet, but is this arguable. In Ruby, we can perform a similar operation, instead using square brackets:
Some people consider Pluto a planet, but this is arguable. In Ruby, we can perform a similar operation, instead using square brackets:


   planets = [ "Mercury", "Venus", "Earth", "Mars", "Jupiter",
   planets = [ :Mercury, :Venus, :Earth, :Mars, :Jupiter,
         "Saturn", "Uranus", "Neptune" ]
         :Saturn, :Uranus, :Neptune ]


The similarities between Java and Ruby with respect to array creation end here. Ruby provides additional convenience operators on array creation that Java lacks.
The similarities between Java and Ruby with respect to array creation end here. Ruby provides additional convenience operators on array creation that Java lacks. Note also that we have introduced the concept of [http://ruby-doc.org/core/classes/Symbol.html symbols], another concept which Java lacks, and thus we use [http://java.sun.com/javase/6/docs/api/java/lang/String.html Strings] for the Java implementation.


For example, Ruby provides an additional way to define an array of strings using the %w notation. It assumes that all elements are strings, but can save typing:
An array in Ruby can also be created by specifying the default value to each element in the array:


  planets = %w[ Mercury Venus Earth Mars Jupiter Saturn Uranus Neptune ]
   planets = Array.new(8, :EmptySpace)
 
An array can also be created by allowing the initialization of a default value to each element in the array:
 
   planets = Array.new(8, "planets")


Furthermore, since Ruby offers range capabilities, one can initialize an array using ranges:
Furthermore, since Ruby offers range capabilities, one can initialize an array using ranges:
Line 45: Line 41:
   digits = Array(1..x)
   digits = Array(1..x)


The above Ruby code creates an array of digits with elements from 1 through 100. The equivalent Java code is far more cumbersome and potentially error-prone, as the programmer must micromanage the details of array indexing:
The above Ruby code creates an array of digits with elements from 1 through <tt>x</tt>. The equivalent Java code is far more cumbersome and potentially error-prone, as the programmer must micromanage the details of array indexing:


   for (int j = 1; j <= x; j++)
   for (int j = 1; j <= x; j++)
Line 52: Line 48:
=== Accessing Array Elements ===
=== Accessing Array Elements ===


Elements in Java and Ruby are accessed in much the same way, though Ruby offers a few additional methods for accessing elements of arrays. In Java, one could access the element "Venus" of the planets array as:
Elements in Java and Ruby are accessed in much the same way, though Ruby again offers a few additional methods for accessing elements of arrays. In Java, one could access the element <tt>:Venus</tt> of the planets array as:


   planets[0];
   planets[0];
Line 64: Line 60:
   planets[-1]
   planets[-1]


returns "Neptune". Finally, Ruby allows the first and last methods to easily get the first and last elements of an array.
returns <tt>:Neptune</tt>. Finally, Ruby provides the <tt>first</tt> and <tt>last</tt> methods to easily get the first and last elements of an array.


Ruby provides several methods, which, in combination with the range operator, provide an elegant way to obtain sub-arrays of an array. In Ruby, for example, we can generate a sub array of the first three elements after "Mercury" of the array with:
Ruby provides several methods, which, in combination with the range operator, provide an elegant way to obtain sub-arrays of an array. In Ruby, for example, we can generate a sub array of the three elements following <tt>:Mercury</tt> from the original array:


   some_planets = planets[1..3]
   some_planets = planets[1..3]
Line 76: Line 72:
Note that the Java method is a half-open interval.
Note that the Java method is a half-open interval.


TODO: Insert some sort of foreach construct here that iterates over an array. Accessing an array element that does not exist.
Both languages allow the programmer to iterate over the array. In Ruby, this can be accomplished with the use of blocks. Let us print out all of the planets in uppercase with a simple one-liner, remembering that each planet is actually represented as a symbol:
 
  planets.each { |p| puts p.to_s.upcase }
 
In Java, one can use the [http://java.sun.com/j2se/1.5.0/docs/guide/language/foreach.html for-each loop] instead, which has similar, but somewhat more verbose syntax:
 
  for (String p : planets)
    System.out.println(s.toUpperCase());
 
Finally, let's look at what happens when we access an element that doesn't exist in the array, such as index 100. Java will throw an <tt>ArrayIndexOutOfBoundsException</tt>. Ruby, on the other hand, returns <tt>nil</tt>.


=== Checking Equality ===
=== Checking Equality ===
Line 82: Line 87:
Ruby provides the <tt>==</tt> method to compare arrays for equality. Given the following code:
Ruby provides the <tt>==</tt> method to compare arrays for equality. Given the following code:


   planets_a = ["Mercury", "Venus", "Earth"]
   planets_a = [:Mercury, :Venus, :Earth]
   planets_b = ["Mercury", "Venus", "Earth"]
   planets_b = [:Mercury, :Venus, :Earth]
   planets_c = ["Earth", "Neptune", "Pluto"]
   planets_c = [:Earth, :Neptune, :Pluto]


It is easy to see that:
It is easy to see that:
Line 97: Line 102:
   planetsC = {"Earth", "Neptune", "Pluto"};
   planetsC = {"Earth", "Neptune", "Pluto"};


the expressions:
The expressions:


   planetsA == planetsB;
   planetsA == planetsB;
   planetsA.equals(planetsB)
   planetsA.equals(planetsB);


both return false. Instead, to get the desired behavior one must again utilize the package <tt>java.util.Arrays</tt>, in particular, the <tt>java.util.Arrays.equals()</tt> method:
both return false. Instead, to get the desired behavior one must again utilize the package <tt>java.util.Arrays</tt>, in particular, the <tt>java.util.Arrays.equals()</tt> method:
Line 106: Line 111:
   java.util.Arrays.equals(planetsA, planetsB)
   java.util.Arrays.equals(planetsA, planetsB)


This is just one example of where Ruby follows the [http://en.wikipedia.org/wiki/Ruby_programming_language principle of least surprise], meaning the language should behave in such a way as to minimize confusion for experienced users.
This is just one example of where Ruby follows the [http://en.wikipedia.org/wiki/Ruby_programming_language principle of least surprise], such that the language behaves in such a way as to minimize confusion for experienced users.


=== Inserting, Removing and Changing Elements ===
=== Inserting, Removing, and Changing Elements ===


Most static languages, including Java, require you to increase the size of an array in order to add elements to it. In Java, one must create a new array of larger size and copy the elements into the newly created array before adding the new element. To compensate for these deficiencies, Java provides classes like ArrayList, but traditional arrays are still subject to this limitation.
Most static languages, including Java, require you to increase the size of an array in order to add elements to it. In Java, one must create a new array of larger size and copy the elements into the newly created array before adding the new element. To compensate for these deficiencies, Java provides classes like [http://java.sun.com/javase/6/docs/api/java/util/ArrayList.html ArrayList], but traditional arrays are still subject to this limitation.


Moreover, while Java provides both [http://java.sun.com/javase/6/docs/api/java/lang/System.html System.arraycopy], and convenience methods in <tt>java.util.Arrays</tt>, the resulting Java code is far from transparent. Ruby, on the other hand, makes such operations as insertion and addition trivial out of the box to implement, largely due to its dynamic handling of arrays.
Moreover, while Java provides both [http://java.sun.com/javase/6/docs/api/java/lang/System.html System.arraycopy], and convenience methods in <tt>java.util.Arrays</tt>, the resulting Java code is far from transparent. Ruby, on the other hand, makes such operations as insertion and addition trivial out of the box to implement, largely due to its dynamic handling of arrays.


Let us first change some elements in an array, without affecting its size, by changing "Venus", "Earth", and "Mars" with "Larry", "Curly", and "Moe". In Java:
Let us first change some elements in an array, without affecting its size, by changing <tt>:Venus</tt>, <tt>:Earth</tt>, and <tt>:Mars</tt> with <tt>:Larry</tt>, <tt>:Curly</tt>, and <tt>:Moe</tt>. In Java:


   planets[1] = "Larry";
   planets[1] = "Larry";
Line 122: Line 127:
All of these can be changed simultaneously in Ruby with a single statement, again, with the help of the range operator:
All of these can be changed simultaneously in Ruby with a single statement, again, with the help of the range operator:


   planets[1..3] = %w[Larry Curly Moe]
   planets[1..3] = [:Larry :Curly :Moe]


Let us now assume that "Pluto" has been defined properly as a planet. It should now be added to the end of the planets array. In Java, one must use the library call [http://java.sun.com/javase/6/docs/api/java/util/Arrays.html java.util.Arrays.copyOf], or roll their own loop. In either case, the algorithm requires the creation of a new array. We choose to implement the former approach:
Let us now assume that <tt>:Pluto</tt> has been defined as a proper planet. It should now be added to the end of the planets array. In Java, one must use the library call [http://java.sun.com/javase/6/docs/api/java/util/Arrays.html java.util.Arrays.copyOf], or roll their own loop. In either case, the algorithm requires the creation of a new array. We choose to implement the former approach:


   planets = java.util.Arrays.copyOf(planets, planets.length + 1);
   planets = java.util.Arrays.copyOf(planets, planets.length + 1);
Line 131: Line 136:
The same operation in Ruby is incredibly straight-forward:
The same operation in Ruby is incredibly straight-forward:


   planets << "Pluto"
   planets << :Pluto


Finally, let us remove "Earth" from the planets array (possibly, it has been destroyed as a result of global warming) in Java. The implementation again is tedious and cumbersome as one must pay attention to the array indexing of each method argument. In fact, a significant amount of time was spent establishing the array boundaries:
Finally, let us remove <tt>:Earth</tt> from the planets array (possibly, it has been destroyed as a result of global warming) in Java. The implementation again is tedious and cumbersome as one must pay attention to the array indexing of each method argument. In fact, a significant amount of time was spent establishing the array boundaries:


   String[] new_planets = new String[planets.length-1];
   String[] new_planets = new String[planets.length-1];
Line 142: Line 147:
In Ruby, one can simply say:
In Ruby, one can simply say:


   planets.delete "Earth"
   planets.delete :Earth


or, with absolute indices:
Or, with absolute indices:


   planets.delete_at(2)
   planets.delete_at(2)
Line 150: Line 155:
== Array Summary ==
== Array Summary ==


To be fair, in Java 6, many of the vanilla array deficiencies have been addresses with additional classes and APIs, in particular, the [http://java.sun.com/javase/6/docs/api/java/util/ArrayList.html ArrayList], which provides methods such as <tt>clear</tt>, <tt>clone</tt>, and <tt>add</tt> that mimic operations found in Ruby. Even with these additional classes, however, the number of pre-defined methods on arrays available in Java lack sorely compared with Ruby. For example, Java offers no built-in equivalent to the Ruby method <tt>uniq</tt>. Finally, in methods that are nearly equivalent between Java and Ruby, the Ruby syntax tends to be less verbose and more declarative.
To be fair, in Java 6, many of the vanilla array deficiencies have been addressed with additional classes and APIs, in particular, the <tt>ArrayList</tt>, which provides methods such as <tt>clear</tt>, <tt>clone</tt>, and <tt>add</tt> that mimic operations found in Ruby. Most experienced Java programmers use <tt>ArrayList</tt> when additional array flexibility is desired. Despite these classes, the number of pre-defined methods on arrays available in Java lack sorely when compared with Ruby. For example, Java offers no built-in equivalent to the Ruby method <tt>uniq</tt>. Finally, in methods that are nearly equivalent between Java and Ruby, the Ruby syntax tends to be less verbose and more declarative.


= Hashes =
= Hashes =


In this section, we provide a side-by-side comparison of common hash operations in Ruby and compare and contrast them with Java.
Ruby provides a hash table through the [http://www.ruby-doc.org/core/classes/Hash.html Hash] class, and offers built-in syntax to access hash table functions. Java provides hash tables through the [http://java.sun.com/javase/6/docs/api/java/util/Hashtable.html java.util.Hashtable] class, which extends [http://java.sun.com/javase/6/docs/api/java/util/Dictionary.html java.util.Dictionary]. Both Java hash tables and Ruby hash tables offer similar functionality at the basic level, but Ruby provides a host of additional methods that make munging hash tables easier.


== Comparison of Common Operations in Ruby and Java ==


== Creating a Hash Table ==
In this section, we provide a side-by-side examination of common hash operations in Ruby and compare and contrast them with Java.


One way to create a hash table in Ruby is to use the new class method:
=== Creating a Hash Table ===
 
In Java, we can initialize an empty hash table called <tt>planets</tt> as follows:
 
  import java.util.Hashtable;
  Hashtable<String, Integer> planets =
      new Hashtable<String, Integer>();
 
A similar operation can be performed in Ruby with:


   planets = Hash.new
   planets = Hash.new


This creates an empty hash table named planets. This is similar to the Java initialization:
If we so choose, Ruby allows us to initialize the planets array through the use of curly braces in a single statement, eliminating the <tt>Hash.new</tt> call entirely:


   import java.util.Hashtable
   planets = {
    :Mercury => 4900,
    :Venus => 12100,
    :Earth => 12800,
    :Mars => 6800,
    :Jupiter => 143000,
    :Saturn => 125000,
    :Uranus => 51100,
    :Neptune =>49500
  }


  Hashtable<String, String> Planets = new Hashtable<String,String>();
[http://www.randomhacks.net/articles/2007/01/20/13-ways-of-looking-at-a-ruby-symbol#9 Ruby symbols are an excellent choice for a hash key]. In Java, the hash table can only be populated with individualized calls to the <tt>put</tt> method, and Strings must be used:
 
  planets.put("Mercury", 4900);
  planets.put("Venus", 12100);
  planets.put("Earth", 12800);
  planets.put("Mars", 6800);
  planets.put("Jupiter", 143000);
  planets.put("Saturn", 125000);
  planets.put("Uranus", 51100);
  planets.put("Neptune", 49500);
 
The hash tables above associate a planet with its diameter in kilometers. If later we decide that <tt>:Pluto</tt> should be a proper planet, it can be added to our hash table like this:
    
    
Hash tables can also be initialized with values in both Java and Ruby.
  planets[:Pluto] = 2300


The following associates "Ring" to "Saturn" and "Big" to "Jupiter" in Ruby.
In Java, we again use the <tt>put</tt> method:


   planets["Ring"]= "Saturn"
   planets.put("Pluto", 2300);
  planets["Big"] = "Jupiter"


or
=== Accessing Table Entries ===


  planets = {"Ring"=> "Saturn","Big"=> "Jupiter"}
In the previous examples we created a hash table named planets in Java and in Ruby.  The hash table contains the names of planets which can be accessed with their corresponding key maps.  In this section we are going to explore the differences in accessing the data in each of these hash tables.


To retrieve the entries from the planets table in Java we can do the following:


As above, the following associates "Ring" to "Saturn" and "Big" to "Jupiter", but in Java.
  planets.get("Saturn");
  planets.get("Jupiter");


  planets.put( "Ring" , "Saturn" );
The same task can be accomplished in Ruby as follows:
  planets.put( "Big" , "Jupiter" );


=== Accessing Table Entries ===
  planets[:Saturn]
  planets[:Jupiter]
 
Both sets of code would retrieve 125000 and 143000.


In the previous examples we created a hashtable named planets in Java and in Ruby.  The hashtable contains the names of planets which can be accessed with their corresponding key maps. In this section we are going to explore the differences in accessing the data in each of these hash tables.
Ruby will return the default value of <tt>nil</tt> if a key is not found, but this default value can be overridden during creation of the hash table. Java always returns <tt>null</tt> when a key is not found.
A common operation is have a key that is not found map to zero.
Here are two different ways to accomplish this in Ruby:


To retrieve the entries from the planets table in Java we can do the following:
  planets = Hash.new(0)
  planets = Hash.new { |h,k| h[k] = 0 }


  planets.get("Ring");
The first method will return a default value of 0 for entries not in the table. The second method will return a default value of 0, and also add the missing entry to the hash table.
  planets.get("Big");


The following code could be used to accomplish the same task in Ruby:
Much like arrays, hash tables can also be iterated over in both Java and Ruby. In Ruby, we can use <tt>each</tt>:


  planets.each {|k,v| puts "#{k}: #{v}" }


  planets["Ring"]
In Java, we once again use for-each to accomplish the same goal, but the resulting code is not nearly as succinct:
  Planets["Big"]


Both sets of code would retrieve Saturn and Jupiter.
  for (String k: planets.keySet())
    System.out.println(k + ": " + planets.get(k));


=== Verifying, Removing, and Replacing Entries ===
=== Verifying, Removing, and Replacing Entries ===


To prevent errors, it is common to verify that an entry or a key is in a table before trying to do an operation on it.  The following Java code will check the hash table planets to see whether or not they are empty, it will check for the key "Big" and the table entry "Saturn".  It will return true if the entry or key is found and false if it is not.
To prevent errors, it is common to verify that an entry or a key is in a table before trying to do an operation on it.  The following Java code will check the hash table <tt>planets</tt> to see whether or not they are empty, check if the key <tt>:Saturn</tt> exists, and check if the value 125000 exists in the hash table.


   planets.isEmpty();
   planets.isEmpty();
   planets.contains("Big");
   planets.containskey("Saturn");
   planets.containskey("Big");
   planets.containsValue(125000);
  Planets.containsValue("Saturn");


Ruby has similar functions which return true if found:
Ruby has similar functions:


   planets.empty?
   planets.empty?
   planets.has_key?("Big")
   planets.has_key?(:Saturn)
   planets.has_value?("Saturn")
   planets.has_value?(125000)


Finding the size of a hashtable is very similar in Java and Ruby.
Finding the size of a hash table is also very similar in Java and Ruby.
Finding the size of hashtable planets in Java:
Finding the size of hash table in Java:


   planets.size();
   planets.size();


And in Ruby:
Compared with Ruby:


   planets.size
   planets.size


The following Java code will remove the key "Big" and its corresponding entry from the hash table planets:
The following Java code will remove the <tt>:Jupiter</tt> and its corresponding entry from the hash table:


   planets.remove ("Big");
   planets.remove ("Jupiter");


Ruby can accomplish the same task with:
Ruby can accomplish the same task with:


   planets.delete("Big");
   planets.delete(:Jupiter);


To replace a key/entry pair in a hash table Java would do the following:
To replace an entry in a hash table in Java, we can simply use the <tt>put</tt> method again. The method returns the previous value or <tt>null</tt> if there is no previous value (yes, this ruins our planet/diameter value to key pairing, but it illustrates replacement operations):


   planets.put("Big","Saturn");
   planets.put("Pluto", 1000);


This is the same syntax we used to populate an empty table.  It returns the previous value or null if no previous value.
In Ruby this operation would look like:


In Ruby it would look like:
  planets[:Pluto] = 1000


  planets.replace({"Ring" =>"Saturn","Big" =>"Jupiter"}){#=> "Ring"=>"Neptune", "Big"=>"Saturn"}
=== Converting a Hash Table Into an Array ===


== Moving a Hash Table Into an Array ==
As a final operation, let us take the <tt>planets</tt> hash table and turn its keys into an array, so that it resembles the original planets array at the beginning of this article. In Ruby, the operation is trivial:


We will use the planets table to populate an array called new_planets.
  planets_array = planets.keys


String[] new_planets = new String[8];
In Java, we must first convert the hash table to a <tt>keySet</tt> and then use it's <tt>toArray</tt> method to return a <tt>planets_array</tt> as a String:
for ( Enumeration e = planets.keys() ; e.hasMoreElements() ; )
 
  {
  String[] planetsArray = planets.keySet().toArray(new String[0]);
  String planet_value = (String) e.nextElement();
 
  new_planets[Integer(e)]=object_value;}
Since <tt>toArray</tt> normally returns an <tt>Object</tt> array, some effort is needed in toying with the types so that the compiler is satisfied.
 
Of course, while this array will have the same contents as the original array, the order of the array elements may be different depending on the internal hash structure.


== Hash Table Summary ==
== Hash Table Summary ==
The difference in creating and using hash tables in Java as opposed to Ruby is mostly a matter of cosmetic syntax. Unlike arrays, hash tables in both Ruby and Java are dynamic data structures, so many of the methods are nearly identical in functionality. As with arrays, the Ruby code is a little more readable and the same task in Java can often be accomplished with less code in Ruby.


= External Links =
= External Links =
Line 265: Line 308:
* [http://oreilly.com/catalog/9780596529864/ Learning Ruby] by Michael Fitzgerald, O'Reilly. Chapters 6 (Arrays) and 7 (Hashes). [Offline Book].
* [http://oreilly.com/catalog/9780596529864/ Learning Ruby] by Michael Fitzgerald, O'Reilly. Chapters 6 (Arrays) and 7 (Hashes). [Offline Book].
* [http://www.ruby-lang.org/en/documentation/ruby-from-other-languages/to-ruby-from-java/ To Ruby From Java]
* [http://www.ruby-lang.org/en/documentation/ruby-from-other-languages/to-ruby-from-java/ To Ruby From Java]
* Source code for Ruby [http://www.ruby-doc.org/doxygen/1.8.4/array_8c-source.html Array class] and [http://www.ruby-doc.org/doxygen/1.8.4/hash_8c-source.html Hash class]. [http://java.sun.com/j2se/1.5.0/source_license.html Sun JDK source code] available once license agreements are accepted.




[[CSC/ECE 517 Summer 2008/wiki1 Assignment|Back to the assignment page]]
[[CSC/ECE 517 Summer 2008/wiki1 Assignment|Back to the assignment page]]

Latest revision as of 23:32, 11 June 2008

Arrays and Hashes

In this article, we compare arrays and hashes in Ruby and Java. Arrays and hashes are built into Ruby. Arrays, of course, are built into Java too, but hashes are only available through library classes. We demonstrate using logically equivalent code sequences the convenience of programming in both languages. Our Ruby descriptions apply to version 1.8, and for Java, we utilize Java 6 to take advantage of the new constructs that eliminate the drudgery of manually boxing, unboxing, and declaring iterators.

Arrays

While both Java and Ruby provide built-in support for arrays, they differ in the operations that can be performed on them. Arrays in both languages are objects, though this association is implicit in Java arrays, where arrays are container objects whose direct superclass is type Object. In Ruby, arrays are easily immediately identifiable as objects and they are represented by the Array class.

Comparison of Common Operations in Ruby and Java

In this section, we provide a side-by-side examination of common array operations in Ruby and compare and contrast them with Java.

Creating an Array

One way to create an array in Ruby is to use the new class method:

 planets = Array.new(8)

This creates an empty array named planets. This is similar to the Java initialization:

 String[] planets = new String[8];

Arrays can also be initialized to values in both Java and Ruby. For example, we can initialize the arrays of planets by entering the following in Java, noting the use of curly braces in array initialization:

 String[] planets = {"Mercury", "Venus", "Earth", "Mars", "Jupiter", 
        "Saturn", "Uranus", "Neptune" };

Some people consider Pluto a planet, but this is arguable. In Ruby, we can perform a similar operation, instead using square brackets:

 planets = [ :Mercury, :Venus, :Earth, :Mars, :Jupiter,
        :Saturn, :Uranus, :Neptune ]

The similarities between Java and Ruby with respect to array creation end here. Ruby provides additional convenience operators on array creation that Java lacks. Note also that we have introduced the concept of symbols, another concept which Java lacks, and thus we use Strings for the Java implementation.

An array in Ruby can also be created by specifying the default value to each element in the array:

 planets = Array.new(8, :EmptySpace)

Furthermore, since Ruby offers range capabilities, one can initialize an array using ranges:

 digits = Array(1..x)

The above Ruby code creates an array of digits with elements from 1 through x. The equivalent Java code is far more cumbersome and potentially error-prone, as the programmer must micromanage the details of array indexing:

  for (int j = 1; j <= x; j++)
    digits[j - 1] = j;

Accessing Array Elements

Elements in Java and Ruby are accessed in much the same way, though Ruby again offers a few additional methods for accessing elements of arrays. In Java, one could access the element :Venus of the planets array as:

 planets[0];

In Ruby, the operation is much the same:

 planets[0]

In addition, Ruby allows one to index using negative indices, so that:

 planets[-1]

returns :Neptune. Finally, Ruby provides the first and last methods to easily get the first and last elements of an array.

Ruby provides several methods, which, in combination with the range operator, provide an elegant way to obtain sub-arrays of an array. In Ruby, for example, we can generate a sub array of the three elements following :Mercury from the original array:

 some_planets = planets[1..3]

Such tasks can also be accomplished in Java to a certain extent, but require the use of the java.util.Arrays package. The equivalent Java code for the simple operation above, for instance, would be:

 String[] somePlanets = java.util.Arrays.copyOfRange(planets, 1, 4)

Note that the Java method is a half-open interval.

Both languages allow the programmer to iterate over the array. In Ruby, this can be accomplished with the use of blocks. Let us print out all of the planets in uppercase with a simple one-liner, remembering that each planet is actually represented as a symbol:

 planets.each { |p| puts p.to_s.upcase }

In Java, one can use the for-each loop instead, which has similar, but somewhat more verbose syntax:

 for (String p : planets)
   System.out.println(s.toUpperCase());

Finally, let's look at what happens when we access an element that doesn't exist in the array, such as index 100. Java will throw an ArrayIndexOutOfBoundsException. Ruby, on the other hand, returns nil.

Checking Equality

Ruby provides the == method to compare arrays for equality. Given the following code:

 planets_a = [:Mercury, :Venus, :Earth]
 planets_b = [:Mercury, :Venus, :Earth]
 planets_c = [:Earth, :Neptune, :Pluto]

It is easy to see that:

 planets_a == planets_b => true
 planets_a == planet_c => false

While Java provides both == and equals(), these operators and methods derive from java.lang.Object and do not provide the intended result. Given the following code:

 planetsA = {"Mercury", "Venus", "Earth"};
 planetsB = {"Mercury", "Venus", "Earth"};
 planetsC = {"Earth", "Neptune", "Pluto"};

The expressions:

 planetsA == planetsB;
 planetsA.equals(planetsB);

both return false. Instead, to get the desired behavior one must again utilize the package java.util.Arrays, in particular, the java.util.Arrays.equals() method:

 java.util.Arrays.equals(planetsA, planetsB)

This is just one example of where Ruby follows the principle of least surprise, such that the language behaves in such a way as to minimize confusion for experienced users.

Inserting, Removing, and Changing Elements

Most static languages, including Java, require you to increase the size of an array in order to add elements to it. In Java, one must create a new array of larger size and copy the elements into the newly created array before adding the new element. To compensate for these deficiencies, Java provides classes like ArrayList, but traditional arrays are still subject to this limitation.

Moreover, while Java provides both System.arraycopy, and convenience methods in java.util.Arrays, the resulting Java code is far from transparent. Ruby, on the other hand, makes such operations as insertion and addition trivial out of the box to implement, largely due to its dynamic handling of arrays.

Let us first change some elements in an array, without affecting its size, by changing :Venus, :Earth, and :Mars with :Larry, :Curly, and :Moe. In Java:

 planets[1] = "Larry";
 planets[2] = "Curly";
 planets[3] = "Moe";

All of these can be changed simultaneously in Ruby with a single statement, again, with the help of the range operator:

 planets[1..3] = [:Larry :Curly :Moe]

Let us now assume that :Pluto has been defined as a proper planet. It should now be added to the end of the planets array. In Java, one must use the library call java.util.Arrays.copyOf, or roll their own loop. In either case, the algorithm requires the creation of a new array. We choose to implement the former approach:

 planets = java.util.Arrays.copyOf(planets, planets.length + 1);
 planets[planets.length - 1] = "Pluto";

The same operation in Ruby is incredibly straight-forward:

 planets << :Pluto

Finally, let us remove :Earth from the planets array (possibly, it has been destroyed as a result of global warming) in Java. The implementation again is tedious and cumbersome as one must pay attention to the array indexing of each method argument. In fact, a significant amount of time was spent establishing the array boundaries:

 String[] new_planets = new String[planets.length-1];
 System.arraycopy(planets, 0, new_planets, 0, 2);
 System.arraycopy(planets, 2 + 1, new_planets, 2, 
     new_planets.length - 2);

In Ruby, one can simply say:

 planets.delete :Earth

Or, with absolute indices:

 planets.delete_at(2)

Array Summary

To be fair, in Java 6, many of the vanilla array deficiencies have been addressed with additional classes and APIs, in particular, the ArrayList, which provides methods such as clear, clone, and add that mimic operations found in Ruby. Most experienced Java programmers use ArrayList when additional array flexibility is desired. Despite these classes, the number of pre-defined methods on arrays available in Java lack sorely when compared with Ruby. For example, Java offers no built-in equivalent to the Ruby method uniq. Finally, in methods that are nearly equivalent between Java and Ruby, the Ruby syntax tends to be less verbose and more declarative.

Hashes

Ruby provides a hash table through the Hash class, and offers built-in syntax to access hash table functions. Java provides hash tables through the java.util.Hashtable class, which extends java.util.Dictionary. Both Java hash tables and Ruby hash tables offer similar functionality at the basic level, but Ruby provides a host of additional methods that make munging hash tables easier.

Comparison of Common Operations in Ruby and Java

In this section, we provide a side-by-side examination of common hash operations in Ruby and compare and contrast them with Java.

Creating a Hash Table

In Java, we can initialize an empty hash table called planets as follows:

 import java.util.Hashtable;
 Hashtable<String, Integer> planets = 
      new Hashtable<String, Integer>();

A similar operation can be performed in Ruby with:

 planets = Hash.new

If we so choose, Ruby allows us to initialize the planets array through the use of curly braces in a single statement, eliminating the Hash.new call entirely:

 planets = {
   :Mercury => 4900,
   :Venus => 12100,
   :Earth => 12800,
   :Mars => 6800,
   :Jupiter => 143000,
   :Saturn => 125000,
   :Uranus => 51100,
   :Neptune =>49500
 }

Ruby symbols are an excellent choice for a hash key. In Java, the hash table can only be populated with individualized calls to the put method, and Strings must be used:

 planets.put("Mercury", 4900);
 planets.put("Venus", 12100);
 planets.put("Earth", 12800);
 planets.put("Mars", 6800);
 planets.put("Jupiter", 143000);
 planets.put("Saturn", 125000);
 planets.put("Uranus", 51100);
 planets.put("Neptune", 49500);

The hash tables above associate a planet with its diameter in kilometers. If later we decide that :Pluto should be a proper planet, it can be added to our hash table like this:

 planets[:Pluto] = 2300

In Java, we again use the put method:

 planets.put("Pluto", 2300);

Accessing Table Entries

In the previous examples we created a hash table named planets in Java and in Ruby. The hash table contains the names of planets which can be accessed with their corresponding key maps. In this section we are going to explore the differences in accessing the data in each of these hash tables.

To retrieve the entries from the planets table in Java we can do the following:

 planets.get("Saturn");
 planets.get("Jupiter");

The same task can be accomplished in Ruby as follows:

 planets[:Saturn]
 planets[:Jupiter]

Both sets of code would retrieve 125000 and 143000.

Ruby will return the default value of nil if a key is not found, but this default value can be overridden during creation of the hash table. Java always returns null when a key is not found. A common operation is have a key that is not found map to zero. Here are two different ways to accomplish this in Ruby:

 planets = Hash.new(0)
 planets = Hash.new { |h,k| h[k] = 0 }

The first method will return a default value of 0 for entries not in the table. The second method will return a default value of 0, and also add the missing entry to the hash table.

Much like arrays, hash tables can also be iterated over in both Java and Ruby. In Ruby, we can use each:

 planets.each {|k,v| puts "#{k}: #{v}" }

In Java, we once again use for-each to accomplish the same goal, but the resulting code is not nearly as succinct:

 for (String k: planets.keySet())
   System.out.println(k + ": " + planets.get(k));

Verifying, Removing, and Replacing Entries

To prevent errors, it is common to verify that an entry or a key is in a table before trying to do an operation on it. The following Java code will check the hash table planets to see whether or not they are empty, check if the key :Saturn exists, and check if the value 125000 exists in the hash table.

 planets.isEmpty();
 planets.containskey("Saturn");
 planets.containsValue(125000);

Ruby has similar functions:

 planets.empty?
 planets.has_key?(:Saturn)
 planets.has_value?(125000)

Finding the size of a hash table is also very similar in Java and Ruby. Finding the size of hash table in Java:

 planets.size();

Compared with Ruby:

 planets.size

The following Java code will remove the :Jupiter and its corresponding entry from the hash table:

 planets.remove ("Jupiter");

Ruby can accomplish the same task with:

 planets.delete(:Jupiter);

To replace an entry in a hash table in Java, we can simply use the put method again. The method returns the previous value or null if there is no previous value (yes, this ruins our planet/diameter value to key pairing, but it illustrates replacement operations):

 planets.put("Pluto", 1000);

In Ruby this operation would look like:

 planets[:Pluto] = 1000

Converting a Hash Table Into an Array

As a final operation, let us take the planets hash table and turn its keys into an array, so that it resembles the original planets array at the beginning of this article. In Ruby, the operation is trivial:

 planets_array = planets.keys

In Java, we must first convert the hash table to a keySet and then use it's toArray method to return a planets_array as a String:

 String[] planetsArray = planets.keySet().toArray(new String[0]);

Since toArray normally returns an Object array, some effort is needed in toying with the types so that the compiler is satisfied.

Of course, while this array will have the same contents as the original array, the order of the array elements may be different depending on the internal hash structure.

Hash Table Summary

The difference in creating and using hash tables in Java as opposed to Ruby is mostly a matter of cosmetic syntax. Unlike arrays, hash tables in both Ruby and Java are dynamic data structures, so many of the methods are nearly identical in functionality. As with arrays, the Ruby code is a little more readable and the same task in Java can often be accomplished with less code in Ruby.

External Links


Back to the assignment page