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 pointersJava 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 pointersJava 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 structsJava 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 pointersJava 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.