Close

Java 12 - Compact Number Formatting support

[Last Updated: Aug 11, 2020]

Java 12 extends existing number formatting APIs to provide support for locale-sensitive compact number formatting.

Now numbers like 1000 (for example) can be formatted as "1K" (short style) or "1 thousand" (long style).

New class CompactNumberFormat

CompactNumberFormat is a concrete subclass of NumberFormat that formats a decimal number in its compact form.

This class is based on SPI pattern. That means we don't need to directly use this class but instead we can get its instance via new factory methods of NumberFormat.

New enum NumberFormat.Style

This enum represents the style for formatting a number within a given NumberFormat instance

public enum Style {
   SHORT,
   LONG
}

Obtaining CompactNumberFormat instance

Following new factory methods of NumberFormat can be used to obtain CompactNumberFormat instance:

package java.text;
 .......
public abstract class NumberFormat extends Format  {
   .......
   //Returns a compact number format for the default Locale with NumberFormat.Style#SHORT
   public static NumberFormat getCompactNumberInstance() {......}

   //Returns a compact number format for the specified Locale and NumberFormat.Style
   public static NumberFormat getCompactNumberInstance(Locale locale, NumberFormat.Style formatStyle) {.....}
   ......
}

Examples

Compact Formatting

package com.logicbig.example;

import java.text.NumberFormat;
import java.util.List;
import java.util.Locale;
import java.util.stream.IntStream;

public class CompactNumberFormatExample {
  public static void main(String[] args) {
      formatForLocale(Locale.US);
      formatForLocale(Locale.GERMANY);
  }

  private static void formatForLocale(Locale locale) {
      List<Integer> numbers = List.of(1000, 1000000, 1000000000);
      System.out.printf("-- SHORT format for locale=%s --%n", locale);
      numbers.stream().forEach((num) -> {
          NumberFormat nf = NumberFormat.getCompactNumberInstance(locale, NumberFormat.Style.SHORT);
          String format = nf.format(num);
          System.out.println(format);
      });
      System.out.printf("-- LONG format for locale=%s --%n", locale);
      numbers.stream().forEach((num) -> {
          NumberFormat nf = NumberFormat.getCompactNumberInstance(locale, NumberFormat.Style.LONG);
          String format = nf.format(num);
          System.out.println(format);
      });
  }
}
-- SHORT format for locale=en_US --
1K
1M
1B
-- LONG format for locale=en_US --
1 thousand
1 million
1 billion
-- SHORT format for locale=de_DE --
1.000
1 Mio.
1 Mrd.
-- LONG format for locale=de_DE --
1 Tausend
1 Million
1 Milliarde

Formatting with Rounding

By default RoundingMode.HALF_EVEN is used:

package com.logicbig.example;

import java.text.NumberFormat;
import java.util.List;
import java.util.Locale;

public class CompactNumberFormatDefaultRounding {
  public static void main(String[] args) {
      formatForLocale(Locale.US);
  }

  private static void formatForLocale(Locale locale) {
      List<Integer> numbers = List.of(1500, 1500000, 1200000000);
      System.out.printf("-- SHORT format for locale=%s --%n", locale);
      numbers.stream().forEach((num) -> {
          NumberFormat nf = NumberFormat.getCompactNumberInstance(locale, NumberFormat.Style.SHORT);
          String format = nf.format(num);
          System.out.println(format);
      });
      System.out.printf("-- LONG format for locale=%s --%n", locale);
      numbers.stream().forEach((num) -> {
          NumberFormat nf = NumberFormat.getCompactNumberInstance(locale, NumberFormat.Style.LONG);
          String format = nf.format(num);
          System.out.println(format);
      });
  }
}
-- SHORT format for locale=en_US --
2K
2M
1B
-- LONG format for locale=en_US --
2 thousand
2 million
1 billion

We can set rounding mode by calling NumberFormat.setRoundingMode(RoundingMode):

package com.logicbig.example;

import java.math.RoundingMode;
import java.text.NumberFormat;
import java.util.List;
import java.util.Locale;

public class CompactNumberFormatExplicitRounding {
  public static void main(String[] args) {
      formatForLocale(Locale.US);
  }

  private static void formatForLocale(Locale locale) {
      List<Integer> numbers = List.of(1500, 1500000, 1200000000);
      System.out.printf("-- SHORT format for locale=%s --%n", locale);
      numbers.stream().forEach((num) -> {
          NumberFormat nf = NumberFormat.getCompactNumberInstance(locale, NumberFormat.Style.SHORT);
          nf.setRoundingMode(RoundingMode.HALF_DOWN);
          String format = nf.format(num);
          System.out.println(format);
      });
      System.out.printf("-- LONG format for locale=%s --%n", locale);
      numbers.stream().forEach((num) -> {
          NumberFormat nf = NumberFormat.getCompactNumberInstance(locale, NumberFormat.Style.LONG);
          nf.setRoundingMode(RoundingMode.HALF_DOWN);
          String format = nf.format(num);
          System.out.println(format);
      });
  }
}
-- SHORT format for locale=en_US --
1K
1M
1B
-- LONG format for locale=en_US --
1 thousand
1 million
1 billion

Parsing

package com.logicbig.example;

import java.text.NumberFormat;
import java.text.ParseException;
import java.util.Locale;

public class CompactNumberFormatParseExample {
  public static void main(String[] args) throws ParseException {
      NumberFormat nf = NumberFormat.getCompactNumberInstance(Locale.US, NumberFormat.Style.SHORT);
      System.out.println("-- short compact parsing --");
      Number num = nf.parse("1K");
      System.out.println(num);
      num = nf.parse("1M");
      System.out.println(num);
      num = nf.parse("1B");
      System.out.println(num);

      System.out.println("-- long compact parsing --");
      nf = NumberFormat.getCompactNumberInstance(Locale.US, NumberFormat.Style.LONG);
      num = nf.parse("1 thousand");
      System.out.println(num);
      num = nf.parse("1 million");
      System.out.println(num);
      num = nf.parse("1 billion");
      System.out.println(num);
  }
}
-- short compact parsing --
1000
1000000
1000000000
-- long compact parsing --
1000
1000000
1000000000

Parsing with grouping

The default parsing behavior does not allow a grouping separator until 'grouping used' is set to true by using setGroupingUsed(boolean). By default 'grouping used' is set to 'false'.

package com.logicbig.example;

import java.text.NumberFormat;
import java.text.ParseException;
import java.util.Locale;

public class CompactNumberFormatParseWithGrouping {
  public static void main(String[] args) throws ParseException {
      parseWithGrouping(false);
      parseWithGrouping(true);
  }

  private static void parseWithGrouping(boolean grouping) throws ParseException {
      System.out.printf("-- grouping=%s  ---%n", grouping);
      NumberFormat nf = NumberFormat.getCompactNumberInstance(Locale.US, NumberFormat.Style.SHORT);
      nf.setGroupingUsed(grouping);
      Number num = nf.parse("1,00K");
      System.out.println(num);
      num = nf.parse("1,00M");
      System.out.println(num);
      num = nf.parse("1,00B");
      System.out.println(num);
  }
}
-- grouping=false  ---
1
1
1
-- grouping=true ---
100000
100000000
100000000000

Example Project

Dependencies and Technologies Used:

  • JDK 12
CompactNumberFormat Examples Select All Download
  • java-12-compact-number-formats
    • src
      • com
        • logicbig
          • example
            • CompactNumberFormatExample.java

    See Also