What is the Java equivalent of pointers?
Pointers are a key feature of the C language. They effectively expose the memory address where a
given piece of data is stored directly to the programmer. They mean that certain operations can be very efficient in C,
at the expense of safety and the potential for bugs due to memory corruption:
if we access data via a pointer, no runtime checks are necessarily made to ensure
that it points to valid or accessible data, leaving us free to corrupt memory, overrun buffers etc. (Actually, modern
C development platforms do have features to mitigate this, but they are not part of the fundamental definition of the language
and not guaranteed to be present and effective.)
Java runs in a Java Virtual Machine (JVM) and, by definition, does not expose direct memory addresses to the programmer. When we set
a particular field on a particular object or pass a parameter into a method, the JVM determines what actual memory address to write the
data to. But the program never "knows" what that memory address is. As far as the Java program is concerned, it is simply accessing a particular field of a
particular object reference. To understand what the equivalent of pointers is in Java, therefore, we must think about the
what we are using pointers for in C, and then use a Java equivalent.
Pointers to string or array data
In C, there is no essential difference between a one-dimensional array and a pointer to an area of memory. Many functions are
passed pointers when functionally speaking, what is required is an array. The Java equivalent is to simply pass an array reference:
C code, using pointers | Java code, using array reference |
int total(int *arr, int len) {
int t = 0;
for (int i = 0; i < len; i++) {
t += arr[i];
}
return t;
}
|
int total(int[] arr) {
int t = 0;
for (int i = 0; i < arr.length; i++) {
t += arr[i];
}
return t;
}
|
Notice that the length parameter is not required, since the length of an array can be queried in Java: an other feature of the fact that an
array is a "managed" object in Java, rather than simply a 'pointer to a memory address'.
Pointers to string data
A common idiomn in C, at least in the C of pre-Unicode days, is to use a char pointer to designate zero-terminated string.
In Java, the equivalent is generally to use a String object or (if the string needs to be mutable) a StringBuffer or StringBuilder.
Tthe two are functionally equivalent, but StringBuilder is a newer, non thread-safe class: it is generally preferable unless you're calling an
older method that specifically requires a StringBuffer (or really need multiple threads to mutate the same string buffer!).
C code, using pointers | Java code, using String reference |
int countLetters(const char *str) {
int t = 0;
char ch;
char *ptr = (char *) str;
while (ch = *(ptr++)) {
if (isLetter(ch)) {
t++;
}
}
return t;
}
|
int countLetters(String str) {
int t = 0;
for (int i = 0, len = str.length(); i < len; i++) {
char ch = str.charAt(i);
if (Character.isLetter(ch)) {
t++;
}
}
return t;
}
|
Pointers to structs
The Java equivalent of a pointer to a struct is generally an object reference. Notice that the dot operator . in Java
is essentially equivalent to the indirection operator -> in C, and that Java has no direct equivalent of . in C.
In other words, in Java, we always pass objects by reference. This contrasts with C, where we have the choice of
"passing by value": in other words, assigning the value of one struct to another and having C make a copy of the struct.
To pass an object by value in Java, we need to explicitly clone it:
C code, using structs | Java equivalent, using objects |
struct a;
a.field1 = 3;
a.field2 = 4;
struct b = a;
|
MyObject a;
a.field1 = 3;
a.field2 = 4;
NyObject b = (MyObject) a.clone();
|
struct *a;
a->field1 = 3;
a->field2 = 4;
struct *b = a;
|
MyObject a;
a.field1 = 3;
a.field2 = 4;
NyObject b = a;
|
Pointers to receive additional return variables
A rarer and less elegant use of pointers in C occurs when we pass a pointer to a memory location that will be populated with
an additional return value. An (equally inelegant) equivalent in Java is to pass a single-element array whose first element
can be populated with the required "return" value. A more elegant and extensible solution is to constrct a 'result' class that can contain
the multiple return values required.
Pointers to pointers
In C, pointers to pointers are commonly used in order to implement arrays of structs or strings, where each struct or string has
been separately allocated. The Java equivalent of a pointer to pointers is generally to use an array of the appropriate
dimension. For example, here are C and Java equivalents of functions that take an array of strings:
C code, using pointer to pointers | Java code, using String array |
int totalLength(const char **strs, int noStrs) {
int i, t = 0;
for (i = 0; i < noStrs; i++) {
t += strlen(strs[i]);
}
return t;
}
|
int totalLength(String[] strs) {
int t = 0;
for (int i = 0; i < strs.length; i++) {
t += strs[i].length();
}
return t;
}
|
Pointers to functions (callbacks and handlers)
C even allows pointers to functions, commonly used to implement callbacks or "plugin" functions. For example, the standard
C library includes the qsort() function which will sort arbitrary data; the caller simply needs to supply a pointer to a function
to sort a given pair of items, and the qsort() function will handle the logistics of determining which items need to be
compared and how they should be ordered in memory. If you have used the C SQLite API, you will also be used to passing pointers to callback functions.
Java has two equivalents to C's function pointers: interfaces and lambdas.
Void pointers
In C, void *ptr is generally used to refer to a "block of memory". There are multiple equivalents of this in Java, depending
on the functional requirement.
Managing blocks of memory, e.g. for data buffered from a file
When we need to refer to a "buffer of data" in Java, we could commonly use either a byte array or an instance of ByteBuffer.
Pointer to a 'struct of multiple potential types'
Prior to C++, one use of void *ptr to effectively perform object-oriented programming in C: we could pass a pointer that
would get case to the appropriate struct type depending on some other 'type' variable:
void intersect(void *obj, int dataType, struct vector v) {
switch (dataType) {
case OBJ_SPHERE :
sphere_intersect((struct Sphere *) obj, v);
break;
case OBJ_CUBE :
cube_intersect((struct Cube *) obj, v);
break;
}
}
Needless to say, in a genuinely object-oriented language such as Java, we would rarely write such code in the first place. Instead,
the equivalent would generally be to use an interface:
public interface IntersectableObject {
void intersect(RayVector v);
}
public class Sphere implements IntersectableObject {
public void intersect(RayVector v) {
// Sphere-specific code here
}
}
public class Cube implements IntersectableObject {
public void intersect(RayVector v) {
// Cube-specific code here
}
}
Then, the Java equivalent of our void *obj is to pass around references to IntersectableObjects, which contain
the relevant object-specific implementations of any methods we require.
Java equivalents of other C features
This section contains information on other Java equivalents of C features such as malloc(), const etc.
If you enjoy this Java programming article, please share with friends and colleagues. Follow the author on Twitter for the latest news and rants.
Editorial page content written by Neil Coffey. Copyright © Javamex UK 2021. All rights reserved.