Java has two different forms of switches:

  • tableswitch, table with keys and labels
  • lookupswitch, table with only labels

tableswitch

Speed: O(1)

tableswitch utilises only labels.

It takes in the int value on top of the stack and uses it to index the table and identify the jump location and then immediately jump.

It is only applicable when there case values are dense and contiguous.

Bytecode Structure

A tableswitch is a variable-length instruction.

Break-down

FormatData TypeNote
tableswitch opcodeunsigned byte
0 - 3 null bytes of paddingzeroed, ensures the following byte begins at an address multiple of 4.
defaultsigned 32 bit integer
lowsigned 32 bit integermust be less than or equal to high.
highsigned 32 bit integer
signed 32 bit integerhigh - low + 1 offsets
treated as a 0-based jump table.

Byte Break-down

tableswitch (0xaa)
0 - 3 byte padding
default byte 1
default byte 2
default byte 3
default byte 4
low byte 1
low byte 2
low byte 3
low byte 4
high byte 1
high byte 2
high byte 3
high byte 4
offsets…stored as 32 bits, similar to default, low & high.

Target Address

The index popped from the stack must be an integer. If it is less than low or greater than high , the target address is calculated by adding either:

  • default to the instruction address
  • the offset (extracted) to instruction address

Pseudo code

int value = popStack();
if (value < lowValue || value > highValue) {
    programCounter += defaultOffset;
} else {
    programCounter += jumpOffset[value - lowValue];
}

Example

return switch (value) {
	case 1 -> "one";
	case 2 -> "two";
	case 3 -> "three";
	default -> "default";
}

would produce a result similar to:

tableswitch 1
		Label1
		Label2
		Label3
	default : Label4
 
Label1:
	ldc "one"
	...
 
Label2:
	ldc "two"
	...
 
Label3:
	ldc "three"
	...
 
Label4:
	ldc "default"
	...

lookupswitch

Speed: O(log n)

lookupswitch utilises both keys and labels.

It takes the int value on top of the stack and compares it against the keys until a match is found. The jump value next to this key is then used to perform the jump.

Bytecode Structure

A lookupswitch is a variable-length instruction.

Break-down

FormatData TypeNote
lookupswitch byteunsigned byte
0 - 3 null bytes of paddingzeroed, ensures the following byte begins at an address multiple of 4.
defaultsigned 32 bit integer
number of pairssigned 32 bit integermust be >= 0.
match-offset pairs…match: signed 32 bit integer
offset: signed 32 bit integer
must in in ascending order.
sorted to support optimal searches, i.e., binary search.

Byte Break-down

lookupswitch (0xab)
0 - 3 byte padding
default byte 1
default byte 2
default byte 3
default byte 4
pairs…stored as two 32 bit signed integers

lookupswitch performs a binary search to identify the key, hence O(log n). This means that the table must be sorted in ascending order (smallest largest).

Example

return switch (value) {
	case 1 -> "one";
	case 204 -> "two-hundred and four";
	case 7912 -> "seven-thousand nine-hundred and twelve";
	default -> "default";
}

would produce a result similar to:

lookupswitch
		1 : Label2
		2 : Label2
		3 : Label3
	default : Label4
 
Label1:
	ldc "one"
	...
 
Label2:
	ldc "two-hundred and four"
	...
 
Label3:
	ldc "seven-thousand nine-hundred and twelve"
	...
 
Label4:
	ldc "default"
	...

Research Sources

https://github.com/ndru83/desugaring-java/blob/master/switch-case-internals.adoc
https://docs.oracle.com/javase/specs/jvms/se24/html/jvms-6.html
https://stackoverflow.com/questions/10287700/difference-between-jvms-lookupswitch-and-tableswitch