DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Please enter at least three characters to search
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

Modernize your data layer. Learn how to design cloud-native database architectures to meet the evolving demands of AI and GenAI workkloads.

Secure your stack and shape the future! Help dev teams across the globe navigate their software supply chain security challenges.

Releasing software shouldn't be stressful or risky. Learn how to leverage progressive delivery techniques to ensure safer deployments.

Avoid machine learning mistakes and boost model performance! Discover key ML patterns, anti-patterns, data strategies, and more.

Related

  • Jakarta WebSocket Essentials: A Guide to Full-Duplex Communication in Java
  • Java Is Greener on Arm
  • Mastering Date/Time APIs: Challenges With Java's Calendar and JDK Date/Time APIs
  • Java 23: What Developers Need to Know

Trending

  • Java’s Next Act: Native Speed for a Cloud-Native World
  • A Guide to Container Runtimes
  • Unlocking AI Coding Assistants Part 3: Generating Diagrams, Open API Specs, And Test Data
  • Building Scalable and Resilient Data Pipelines With Apache Airflow
  1. DZone
  2. Coding
  3. Java
  4. JDK 17 - Switch Case Performance

JDK 17 - Switch Case Performance

The new switch statement is going to be very useful but if we can write our code both ways e.g., old and new, which will be preferred? Let's compare the performance of both implementations.

By 
Artun Kutluk user avatar
Artun Kutluk
·
Dec. 07, 21 · Analysis
Likes (6)
Comment
Save
Tweet
Share
13.9K Views

Join the DZone community and get the full member experience.

Join For Free

Switch statements in Java have evolved over time. Switch statements supported primitive types at the beginning, then came along String,  now JDK17 stable release is out and we have pattern matching which allows us to pass different object types to the switch statement.

We can now have switch statements like this: (example taken from Oracle JavaSE 17 docs)

Java
 
record Point(int x, int y) { }
enum Color { RED, GREEN, BLUE; }
...
    static void typeTester(Object obj) {
        switch (obj) {
            case null     -> System.out.println("null");
            case String s -> System.out.println("String");
            case Color c  -> System.out.println("Color with " + c.values().length + " values");
            case Point p  -> System.out.println("Record class: " + p.toString());
            case int[] ia -> System.out.println("Array of int values of length" + ia.length);
            default       -> System.out.println("Something else");
        }
    }


It's obvious that the new switch statement will be very useful. It will change how we code, make the code cleaner and shorter, make it more readable in some cases, and so on. However, if we come to a point where we can write our code both ways (the old way with primitives and so on, and the new way with pattern matching), which one will be preferred? Coding time, readability, memory usage, and performance are the criteria we should consider. Let's look at the performance of the two implementation ways and compare them.

There will be unlimited types of implementations with the new pattern matching switch but we will try to focus on the switch case by creating a simple scenario.

Let's have four classes (ClassAddition, ClassSubtraction, ClassMultiplication, ClassDivision) extended from abstract class ClassOperation which has a number of variables and typeId representing the operation (1: add, 2:subtract, 3:multiply, 4:divide) that will be done. 

The code for this looks like this:

Java
 
public abstract class ClassOperation {

    private int typeId;
    private double number;

    public int getTypeId() {return typeId;}
    public void setTypeId(int typeId) {this.typeId = typeId;}

    public double getNumber() {return number;}
    public void setNumber(double number) {this.number = number;}
}


Java
 
public class ClassAddition extends ClassOperation {

    public ClassAddition(double number){
        setTypeId(OperationTypeEnum.ADDITION.getId());
        setNumber(number);
    }
}


Java
 
public class ClassSubtraction extends ClassOperation {

    public ClassSubtraction(double number){
        setTypeId(OperationTypeEnum.SUBTRACTION.getId());
        setNumber(number);
    }
}


Java
 
public class ClassMultiplication extends ClassOperation{

    public ClassMultiplication(double number){
        setTypeId(OperationTypeEnum.MULTIPLICATION.getId());
        setNumber(number);
    }
}


Java
 
public class ClassDivision extends ClassOperation{

    public ClassDivision(double number){
        setTypeId(OperationTypeEnum.DIVISION.getId());
        setNumber(number);
    }
}


Java
 
public enum OperationTypeEnum {

    ADDITION(1),
    SUBTRACTION(2),
    MULTIPLICATION(3),
    DIVISION(4);


    OperationTypeEnum(int id){
        this.id = id;
    }

    public static OperationTypeEnum findById(int id){
        for (OperationTypeEnum ot : OperationTypeEnum.values()){
            if(ot.getId() == id){
                return ot;
            }
        }
        return null;
    }

    private int id;

    public int getId(){
        return id;
    }
}


Now let's create the methods that will fill a list randomly with these classes with random values.  

Java
 
private static void prepareData(long listSize){

  operationList.clear();

  for (long i = 0; i < listSize; i++){
    int typeId = (int) Math.floor(Math.random() * 4 + 1);
    double number = Math.floor(Math.random()*5 + 1);

    operationList.add(getObject(typeId, number));
  }

}


Java
 
private static Object getObject(int typeId, double number){

  OperationTypeEnum ot = OperationTypeEnum.findById(typeId);

  switch (ot){
    case ADDITION:
      return new ClassAddition(number);
    case SUBTRACTION:
      return new ClassSubtraction(number);
    case MULTIPLICATION:
      return new ClassMultiplication(number);
    case DIVISION:
      return new ClassDivision(number);
    default:
      return null;
  }
}


Afterward, let's calculate the total of these lists in both switch statement styles.

The New Switch:

Java
 
private static void calculateUsingNewSwitch(){

  BigDecimal total = BigDecimal.ZERO;

  long startTime = System.nanoTime();

  for (Object obj : operationList){

    switch(obj){
      case ClassAddition ca -> total = total.add(new BigDecimal(ca.getNumber()));
      case ClassSubtraction cs -> total = total.subtract(new BigDecimal(cs.getNumber()));
      case ClassMultiplication cm -> total = total.multiply(new BigDecimal(cm.getNumber()));
      case ClassDivision cd -> total = total.divide(new BigDecimal(cd.getNumber()), 2, RoundingMode.HALF_UP);
      default -> System.out.println("unknown type for new switch");
    };
  }

  long endTime = System.nanoTime();

  double milliseconds = (double) (endTime - startTime) / 1_000_000;

  System.out.println (operationList.size() + " -> new switch -> took: " + milliseconds);
}


The Old Switch:

Java
 
private static void calculateUsingOldSwitch(){

  BigDecimal total = BigDecimal.ZERO;

  long startTime = System.nanoTime();

  for (Object obj : operationList){

    OperationTypeEnum ot = OperationTypeEnum.findById(((ClassOperation)obj).getTypeId());

    switch(ot){
      case ADDITION:
        total = total.add(new BigDecimal(((ClassAddition) obj).getNumber()));
        break;
      case SUBTRACTION:
        total = total.subtract(new BigDecimal(((ClassSubtraction)obj).getNumber()));
        break;
      case MULTIPLICATION:
        total = total.multiply(new BigDecimal(((ClassMultiplication)obj).getNumber()));
        break;
      case DIVISION:
        total = total.divide(new BigDecimal(((ClassDivision)obj).getNumber()), 2, RoundingMode.HALF_UP);
        break;
      default: System.out.println("unknown type for old switch");
        break;
    };
  }

  long endTime = System.nanoTime();

  double milliseconds = (double) (endTime - startTime) / 1_000_000;

  System.out.println (operationList.size() + " -> old switch -> took: "  + milliseconds);
}


Now, let's run our tests 5 times for 5 different sets:

  • 10.000
  • 100.000
  • 1.000.000
  • 10.000.000
  • 100.000.000
Java
 
public static List<Object> operationList;

public static void main(String[] args) {

  operationList = new ArrayList<>();

  for (int t = 1; t <= 5; t++) {
    long size = 10000;

    for (int i = 1; i <= 5; i++) {
      try {
        runTest(size);
      } catch (Exception e) {
        System.out.println(e.toString());
      }
      size = size * 10;
    }
  }
}-


And the results are:


RUN2 RUN3 RUN4 RUN5 AVG
NEW OLD NEW OLD NEW OLD NEW OLD NEW OLD
10.000 6,7 3,1 4,2 3,0 3,0 1,8 2,2 2,5 4,0 2,6
100.000 41,1 36,9 38,9 36,4 29,8 34,6 18,6 18,5 32,1 31,6
1.000.000 367,5 387,6 355,4 247,0 499,2 562,8 341,3 345,7 390,9 385,8
10.000.000 5.512,8 5.070,6 4.750,4 4.192,6 6.940,2 6.836,5 4.450,2 3.735,6 5.413,4 4.958,8
100.000.000 95.391,9 95.331,8 101.763,1 100.890,0 106.325,7 102.622,2 182.330,1 186.933,0 121.452,7 121.444,2


New switch: Results of 5 test runs for 5 different sets


New switch: Results of 5 test runs for 5 different sets


As you may have noticed, typecast in the old switch was not necessary. They were done in order to have the same operations being executed on both examples so the only difference would be the switch statement itself.  

Let's make the following changes get rid of extra type casts and re-run the test:

  • Change the type of the List from Object to ClassOperation
  • Change the return type of getObject from Object to ClassOperation
  • Remove the ClassOperation type casts from both switches
  • Remove the remaining type casts from the old switch

After these steps, our old switch calculation method would look like this:

Java
 
private static void calculateUsingOldSwitch(){

  BigDecimal total = BigDecimal.ZERO;

  long startTime = System.nanoTime();

  for (ClassOperation obj : operationList){

    OperationTypeEnum ot = OperationTypeEnum.findById(obj.getTypeId());

    switch(ot){
      case ADDITION:
        total = total.add(new BigDecimal(obj.getNumber()));
        break;
      case SUBTRACTION:
        total = total.subtract(new BigDecimal(obj.getNumber()));
        break;
      case MULTIPLICATION:
        total = total.multiply(new BigDecimal(obj.getNumber()));
        break;
      case DIVISION:
        total = total.divide(new BigDecimal(obj.getNumber()), 2, RoundingMode.HALF_UP);
        break;
      default: System.out.println("unknown type for old switch");
        break;
    };
  }

  long endTime = System.nanoTime();

  double milliseconds = (double) (endTime - startTime) / 1_000_000;

  System.out.println (operationList.size() + " -> old switch -> took: "  + milliseconds);
}


If we re-run the tests, the result is:


RUN2 RUN3 RUN4 RUN5 AVG
NEW OLD NEW OLD NEW OLD NEW OLD NEW OLD
10.000 1,5 1,5 1,7 1,7 1,7 1,7 1,6 1,6 1,6 1,6
100.000 21,6 21,4 23,6 22,1 23,0 22,5 25,1 24,5 23,3 22,6
1.000.000 244,7 229,6 271,8 268,5 278,9 255,4 282,7 235,5 269,5 247,2
10.000.000 6.792,8 6.293,1 4.858,4 4.563,7 5.288,4 4.384,5 4.994,7 4.541,1 5.483,6 4.945,6
100.000.000 103.260,7 102.734,4 132.283,3 132.437,4 227.572,2 223.576,9 74.045,6 71.794,5 134.290,5 132.635,8


Old switch: Results of test re-runs for 5 different sets


New switch: Results of test re-runs for 5 different sets

As you can see from the results, the performance of the new switch and the old switch with type casts can be considered as same. The performance of the old switch without typecast is slightly better. 

When it comes to a point where we can choose one of the two ways, which one we choose won't make a big difference as performance. Of course, our result can not imply that it will always be the same for every usage but it can give an idea.

Java (programming language) Java Development Kit

Opinions expressed by DZone contributors are their own.

Related

  • Jakarta WebSocket Essentials: A Guide to Full-Duplex Communication in Java
  • Java Is Greener on Arm
  • Mastering Date/Time APIs: Challenges With Java's Calendar and JDK Date/Time APIs
  • Java 23: What Developers Need to Know

Partner Resources

×

Comments
Oops! Something Went Wrong

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends:

Likes
There are no likes...yet! 👀
Be the first to like this post!
It looks like you're not logged in.
Sign in to see who liked this post!