There are several differences between HashMap
and Hashtable
in Java:
Hashtable
is synchronized, whereas HashMap
is not. This makes HashMap
better for non-threaded applications, as unsynchronized Objects typically perform better than synchronized ones.
Hashtable
does not allow null
keys or values. HashMap
allows one null
key and any number of null
values.
One of HashMap's subclasses is LinkedHashMap
, so in the event that you'd want predictable iteration order (which is insertion order by default), you could easily swap out the HashMap
for a LinkedHashMap
. This wouldn't be as easy if you were using Hashtable
.
Since synchronization is not an issue for you, I'd recommend HashMap
. If synchronization becomes an issue, you may also look at ConcurrentHashMap
.
Java is always pass-by-value. Unfortunately, when we deal with objects we are really dealing with object-handles called references which are passed-by-value as well. This terminology and semantics easily confuse many beginners.
It goes like this:
public static void main(String[] args) {
Dog aDog = new Dog("Max");
Dog oldDog = aDog;
// we pass the object to foo
foo(aDog);
// aDog variable is still pointing to the "Max" dog when foo(...) returns
aDog.getName().equals("Max"); // true
aDog.getName().equals("Fifi"); // false
aDog == oldDog; // true
}
public static void foo(Dog d) {
d.getName().equals("Max"); // true
// change d inside of foo() to point to a new Dog instance "Fifi"
d = new Dog("Fifi");
d.getName().equals("Fifi"); // true
}
In the example above aDog.getName()
will still return "Max"
. The value aDog
within main
is not changed in the function foo
with the Dog
"Fifi"
as the object reference is passed by value. If it were passed by reference, then the aDog.getName()
in main
would return "Fifi"
after the call to foo
.
Likewise:
public static void main(String[] args) {
Dog aDog = new Dog("Max");
Dog oldDog = aDog;
foo(aDog);
// when foo(...) returns, the name of the dog has been changed to "Fifi"
aDog.getName().equals("Fifi"); // true
// but it is still the same dog:
aDog == oldDog; // true
}
public static void foo(Dog d) {
d.getName().equals("Max"); // true
// this changes the name of d to be "Fifi"
d.setName("Fifi");
}
In the above example, Fifi
is the dog's name after call to foo(aDog)
because the object's name was set inside of foo(...)
. Any operations that foo
performs on d
are such that, for all practical purposes, they are performed on aDog
, but it is not possible to change the value of the variable aDog
itself.
For more information on pass by reference and pass by value, consult the following SO answer: https://stackoverflow.com/a/430958/6005228. This explains more thoroughly the semantics and history behind the two and also explains why Java and many other modern languages appear to do both in certain cases.
Best Answer
Update: Since this answer was posted, some of the tools available have changed. After the original answer, there is an update including information on how to build the example with current tools.
It isn't quite as simple as compiling to a jar and calling the internal methods. There do seem to be a few tricks to make it all work though. Here's an example of a simple Clojure file that can be compiled to a jar:
If you run it, you should see something like:
And here's a Java program that calls the
-binomial
function in thetiny.jar
.It's output is:
The first piece of magic is using the
:methods
keyword in thegen-class
statement. That seems to be required to let you access the Clojure function something like static methods in Java.The second thing is to create a wrapper function that can be called by Java. Notice that the second version of
-binomial
has a dash in front of it.And of course the Clojure jar itself must be on the class path. This example used the Clojure-1.1.0 jar.
Update: This answer has been re-tested using the following tools:
The Clojure Part
First create a project and associated directory structure using Leiningen:
Now, change to the project directory.
In the project directory, open the
project.clj
file and edit it such that the contents are as shown below.Now, make sure all of the dependencies (Clojure) are available.
You may see a message about downloading the Clojure jar at this point.
Now edit the Clojure file
C:\projects\com.domain.tiny\src\com\domain\tiny.clj
such that it contains the Clojure program shown in the original answer. (This file was created when Leiningen created the project.)Much of the magic here is in the namespace declaration. The
:gen-class
tells the system to create a class namedcom.domain.tiny
with a single static method calledbinomial
, a function taking two integer arguments and returning a double. There are two similarly named functionsbinomial
, a traditional Clojure function, and-binomial
and wrapper accessible from Java. Note the hyphen in the function name-binomial
. The default prefix is a hyphen, but it can be changed to something else if desired. The-main
function just makes a couple of calls to the binomial function to assure that we are getting the correct results. To do that, compile the class and run the program.You should see output shown in the original answer.
Now package it up in a jar and put it someplace convenient. Copy the Clojure jar there too.
The Java Part
Leiningen has a built-in task,
lein-javac
, that should be able to help with the Java compilation. Unfortunately, it seems to be broken in version 2.1.3. It can't find the installed JDK and it can't find the Maven repository. The paths to both have embedded spaces on my system. I assume that is the problem. Any Java IDE could handle the compilation and packaging too. But for this post, we're going old school and doing it at the command line.First create the file
Main.java
with the contents shown in the original answer.To compile java part
Now create a file with some meta-information to add to the jar we want to build. In
Manifest.txt
, add the following textNow package it all up into one big jar file, including our Clojure program and the Clojure jar.
To run the program:
The output is essentially identical to that produced by Clojure alone, but the result has been converted to a Java double.
As mentioned, a Java IDE will probably take care of the messy compilation arguments and the packaging.