These are very similar to what you are used to in C++. A few differences worth pointing out:
C++ does not have a byte
type, because the char
type in C++ is defined to be the size of an integer used to hold a single character in the most commonly used character set encoding on a given architecture - which is pretty much the definition of a “byte”.
A C++ char
is almost always an 8-bit quantity - enough to hold a single seven-bit ASCII character plus an 8th bit for common extensions. As multinational markets and user groups have become more important, however, the old 8-bit character code has become increasingly restrictive. Java has therefore embraced 16-bit Unicode as the basis of a char
. This makes Java an easier language in which to do international work, but means that many traditional C++ “tricks” based on characters being 8-bit integers will no longer work. For example, a C++ programmer might use the code
c = c + ('A' - 'a');
to convert a character c
from lower-case to upper-case. But in international alphabets, this computation might not work. In fact, it’s not even guaranteed that the familiar definitions of upper and lower case even apply.
Java does not unsigned int
, unsigned
long
, unsigned short
, etc., variants that can be found in C++.
Java has very precise definitions of how many bits occur in each of its integer and floating point types. This is a marked contrast to C++, where the size of an int
varies from one operating system to the next, where a long
might have more bits than an int
, but then again, it might not. (C++ only requires that a long
have no fewer bits than an ordinary int
.)
This is a major boon to portability. If you have an application where you know that you need integers that range from 0..100000, for example, there is simply no portable way to declare a variable in C++ that guarantees that you will have enough bits to hold that number.
The C++ bool
type is called boolean
in Java. Why? Probably just to annoy people.
The biggest difference between Java and C++ arrays is simply that Java arrays “know” how many elements they have room for. You retrieve this information with the length
property.
Of course, just because you have created an array capable of holding 10000 elements, that doesn’t mean that you actually have filled in that many elements. You may still need to keep track of just how many elements of an array you are actually using at any given point in your code.
Declaring a Variable to Refer to an Array
Take note of the syntax: T[]
instead of T*
. Actually, T[]
is legal in many contexts in C++ as well, but is not used as widely.
Creating, Initializing, and Accessing an Array
Notice that all arrays in Java are allocated on the heap via the new
operation. You will recognize this as the equivalent of dynamic allocation of arrays in C++. The C++ option of doing a static allocation:
{
int array[100];
⋮
}
is not available in Java. This is part of a more general pattern of Java practice. Everything is that is not a single simple primitive will wind up being allocated on the heap.
There’s not much surprising here. There are a few operators of C++ that you won’t find in Java:
& does not get used to take the “address of” an object. Surprisingly, that’s not because we wouldn’t do such a ting in Java. Actually, we will see later that working with addresses of things is so common in Java that it would simply be ridiculous if we had to write an operator in every time we did it.
For similar reasons, the * is only used in its binary sense - as a multiplication operator. It’s unary sense of dereferencing a pointer (e.g., *ptr
) does not occur in Java.
The -> operator of C++ becomes simply ‘.’ in Java. That may seem strange to C++ programmers, who see a big difference between X.member
and ptr->member
, But in Java, all selections of members are done via a pointer-like mechanism, so if Java had both ‘->’ and ‘.’, then we would never have occasion to use the ‘.’. So Java went with the shorter of the two options.
<<
and >>
have dual meanings in C++. They can be used as “bit shifting” operations or as I/O operations. Java only uses them as bit shifters.
The biggest difference between operators in C++ and Java is that, in C++, each operator is a shorthand for a function name, and because function names can be overloaded, that means that operators can be overloaded as well. In Java, operators are not a shorthand and programmers cannot overload the operators.
The biggest difference for C++ programmers is the enhanced for
statement. So if a C++ programmer were to process an array like this:
void foo (int[] array, int n)
{
for (int i = 0; i < n; ++i) {
int x = array[i];
cout << x << endl;
}
}
a Java programmer has the option of doing:
void foo (int[] array)
{
for (int i = 0; i < array.length; ++i) {
int x = array[i];
System.out.println(x);
}
}
or
void foo (int[] array)
{
for (int x: array) {
System.out.println(x);
}
}
Later, when we start working with different kinds of container types, we might contrast the processing of lists in C++:
void bar (list<int> seq)
{
for (list<int>::iterator p = seq.begin(); p != seq.end(); ++p) {
int x = *p;
doSomethingWith(x);
}
}
with its closest equivalent in Java:
void bar (List<int> seq)
{
for (Iterator<int> p = seq.iterator(); p.hasNext();) {
int x = p.next();
doSomethingWith(x);
}
}
or the abbreviated form that uses the enhanced for
:
void bar (List<int> seq)
{
for (int x: seq) {
doSomethingWith(x);
}
}
Enhanced for
and C++11
Interestingly enough, the “enhanced for
” has been written into the new C++11 standard (which compilers are just starting to implement), so C++ programmers will soon be able to use it with any container that supports iterators:
void bar (list<int> seq)
{
for (int x: seq) { // C++11
doSomethingWith(x);
}
}
In C++, this new feature is known as the “range-based for loop”.
There’s even a new way to declare arrays that let’s you do the same kind of loop:
void bar (
{
std::array<int,10> arr;
⋮
for (int x: arr) { // C++11
doSomethingWith(x);
}
}
Limitations of the enhanced for
The enhanced for
loop does lead to simpler, more readable code in many cases, it does have some limitations, however.
First, it works best with loops that are processing an entire container, not simply a portion of it. For example, you can’t express this more complicated loop condition:
boolean contains (List<int> seq, int key)
{
boolean found = false;
for (Iterator<int> p = seq.iterator(); (!found) && p.hasNext();) {
int x = p.next();
found = (x == key);
}
return found;
}
except by jumping out of the middle of the loop:
boolean contains (List<int> seq, int key)
{
boolean found = false;
for (Iterator<int> p = seq.iterator(); (!found) && p.hasNext();) {
int x = p.next();
found = (x == key);
if (found)
break;
}
return found;
}
Not awful, but arguably not as readable as the original.
More serious problems arise when you need to traverse multiple data structures at the same time:
boolean equalTo (List<int> left, List<int> right)
{
boolean found = false;
Iterator<int> p = left.iterator();
Iterator<int> q = right.iterator();
int x = 0;
int y = 0;
while ((x == y) && p.hasNext() && q.hasNext()) {
x = p.next();
y = q.next();
}
return x == y && (!p.hasNext()) && (!q.hasNext());
}
Using the enhanced for, the best we could do would be to eliminate one set of iterator manipulations:
boolean equalTo (List<int> left, List<int> right)
{
boolean found = false;
Iterator<int> q = right.iterator();
int y = 0;
while (int x: left) {
if (!q.hasNext)
return false;
y = q.next();
if (x != y)
return false;
}
return !q.hasNext();
}
In my opinion, the asymmetry in the handling of the two lists makes the second version much harder to understand.
These are also available in C++ as well, though you may not have seen them very often.