Everything Java

← Back to blog

Published on Wed Jan 15 2025 10:00:00 GMT+0000 (Coordinated Universal Time) by Purusothaman Ramanujam

Number Utilities in Apache Commons Lang

Introduction

Working with numbers in Java can be tricky, especially when dealing with parsing, validation, and conversion between different numeric types. Apache Commons Lang’s NumberUtils provides a comprehensive set of utilities that make number operations safer, more convenient, and more robust.

In this comprehensive guide, you’ll learn how to use NumberUtils effectively with detailed examples and real-world use cases.

Adding Apache Commons Lang to Your Project

Maven Dependency

<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.13.0</version>
</dependency>

Gradle Dependency

implementation 'org.apache.commons:commons-lang3:3.13.0'

Basic Number Operations

Null-Safe Number Handling

One of the biggest advantages of NumberUtils is its null-safe operations:

import org.apache.commons.lang3.math.NumberUtils;
public class NumberUtilsBasics {
public static void main(String[] args) {
Integer num1 = 42;
Integer num2 = null;
String str1 = "123";
String str2 = "not a number";
String str3 = null;
// Null-safe maximum/minimum
System.out.println(NumberUtils.max(num1, num2)); // 42
System.out.println(NumberUtils.min(num1, num2)); // 42
// Null-safe comparison
System.out.println(NumberUtils.compare(num1, num2)); // 1 (num1 is greater)
System.out.println(NumberUtils.compare(num2, num1)); // -1 (num2 is less)
System.out.println(NumberUtils.compare(num2, num2)); // 0 (both null)
}
}

Number Creation and Parsing

public class NumberParsing {
public static void main(String[] args) {
String validNumber = "123.45";
String invalidNumber = "abc";
String nullString = null;
// Create numbers with default values
System.out.println(NumberUtils.createInteger("123")); // 123
System.out.println(NumberUtils.createLong("123")); // 123L
System.out.println(NumberUtils.createDouble("123.45")); // 123.45
System.out.println(NumberUtils.createBigDecimal("123.45")); // 123.45
// Parse with default values
System.out.println(NumberUtils.toInt("123", -1)); // 123
System.out.println(NumberUtils.toInt(invalidNumber, -1)); // -1
System.out.println(NumberUtils.toInt(nullString, -1)); // -1
System.out.println(NumberUtils.toLong("123", -1L)); // 123
System.out.println(NumberUtils.toDouble("123.45", -1.0)); // 123.45
System.out.println(NumberUtils.toFloat("123.45", -1.0f)); // 123.45f
}
}

Number Validation

Checking if Strings Can Be Converted to Numbers

public class NumberValidation {
public static void main(String[] args) {
String[] testStrings = {
"123", // Valid integer
"123.45", // Valid decimal
"-123", // Valid negative
"1.23e5", // Valid scientific notation
"abc", // Invalid
"123abc", // Invalid
" 123 ", // Valid (whitespace)
"", // Invalid
null // Invalid
};
for (String str : testStrings) {
System.out.printf("'%s' is creatable: %s%n",
str, NumberUtils.isCreatable(str));
}
// Specific type checks
System.out.println(NumberUtils.isDigits("123")); // true
System.out.println(NumberUtils.isDigits("123.45")); // false
System.out.println(NumberUtils.isParsable("123")); // true
System.out.println(NumberUtils.isParsable("123.45")); // true
System.out.println(NumberUtils.isParsable("abc")); // false
}
}

Advanced Validation

public class AdvancedValidation {
public static void main(String[] args) {
// Check if number is finite
System.out.println(NumberUtils.isFinite(123.45)); // true
System.out.println(NumberUtils.isFinite(Double.POSITIVE_INFINITY)); // false
// Check if number is positive/negative
System.out.println(NumberUtils.isPositive(123)); // true
System.out.println(NumberUtils.isPositive(-123)); // false
System.out.println(NumberUtils.isNegative(-123)); // true
// Check if number is zero
System.out.println(NumberUtils.isZero(0)); // true
System.out.println(NumberUtils.isZero(0.0)); // true
System.out.println(NumberUtils.isZero(123)); // false
}
}

Number Conversion

Safe Type Conversion

public class NumberConversion {
public static void main(String[] args) {
// Convert between numeric types safely
Integer intValue = 123;
Long longValue = 456L;
Double doubleValue = 789.0;
// Convert with default values
System.out.println(NumberUtils.toInt(intValue, -1)); // 123
System.out.println(NumberUtils.toLong(intValue, -1L)); // 123L
System.out.println(NumberUtils.toDouble(intValue, -1.0)); // 123.0
// Convert from String with type checking
System.out.println(NumberUtils.toInt("123", -1)); // 123
System.out.println(NumberUtils.toInt("123.45", -1)); // -1 (not an integer)
System.out.println(NumberUtils.toDouble("123.45", -1.0)); // 123.45
// Convert with radix (base)
System.out.println(NumberUtils.toInt("FF", 16, -1)); // 255
System.out.println(NumberUtils.toInt("1010", 2, -1)); // 10
}
}

Array Operations

import java.util.Arrays;
public class NumberArrayOperations {
public static void main(String[] args) {
int[] numbers = {3, 7, 2, 9, 1, 5};
// Find min/max in array
System.out.println(NumberUtils.min(numbers)); // 1
System.out.println(NumberUtils.max(numbers)); // 9
// Find min/max with null safety
Integer[] nullableNumbers = {3, null, 7, 2, null, 9};
System.out.println(NumberUtils.min(nullableNumbers)); // 2
System.out.println(NumberUtils.max(nullableNumbers)); // 9
// Convert array types
String[] stringNumbers = {"1", "2", "3", "4", "5"};
int[] intArray = NumberUtils.toInt(stringNumbers);
System.out.println(Arrays.toString(intArray)); // [1, 2, 3, 4, 5]
// Convert with default values
String[] mixedStrings = {"1", "abc", "3", "def", "5"};
int[] convertedArray = NumberUtils.toInt(mixedStrings, -1);
System.out.println(Arrays.toString(convertedArray)); // [1, -1, 3, -1, 5]
}
}

Mathematical Operations

Advanced Math Functions

public class MathOperations {
public static void main(String[] args) {
// Power operations
System.out.println(NumberUtils.pow(2, 3)); // 8
System.out.println(NumberUtils.pow(5, 2)); // 25
// Greatest Common Divisor
System.out.println(NumberUtils.gcd(12, 18)); // 6
System.out.println(NumberUtils.gcd(48, 18)); // 6
// Least Common Multiple
System.out.println(NumberUtils.lcm(12, 18)); // 36
System.out.println(NumberUtils.lcm(8, 12)); // 24
// Factorial
System.out.println(NumberUtils.factorial(5)); // 120
System.out.println(NumberUtils.factorial(0)); // 1
// Next/Previous power of 2
System.out.println(NumberUtils.nextPowerOfTwo(7)); // 8
System.out.println(NumberUtils.nextPowerOfTwo(8)); // 8
System.out.println(NumberUtils.nextPowerOfTwo(9)); // 16
}
}

Range and Boundary Operations

public class RangeOperations {
public static void main(String[] args) {
// Check if number is in range
System.out.println(NumberUtils.isInRange(5, 1, 10)); // true
System.out.println(NumberUtils.isInRange(15, 1, 10)); // false
System.out.println(NumberUtils.isInRange(1, 1, 10)); // true (inclusive)
System.out.println(NumberUtils.isInRange(10, 1, 10)); // true (inclusive)
// Clamp values to range
System.out.println(NumberUtils.clamp(5, 1, 10)); // 5
System.out.println(NumberUtils.clamp(15, 1, 10)); // 10
System.out.println(NumberUtils.clamp(0, 1, 10)); // 1
// Check if number is between (exclusive)
System.out.println(NumberUtils.isBetween(5, 1, 10)); // true
System.out.println(NumberUtils.isBetween(1, 1, 10)); // false
System.out.println(NumberUtils.isBetween(10, 1, 10)); // false
}
}

Real-World Examples

Configuration Value Parsing

public class ConfigurationParser {
public static class Config {
private int port;
private long timeout;
private double threshold;
public Config(int port, long timeout, double threshold) {
this.port = port;
this.timeout = timeout;
this.threshold = threshold;
}
@Override
public String toString() {
return String.format("Config{port=%d, timeout=%d, threshold=%.2f}",
port, timeout, threshold);
}
}
public static Config parseConfig(String portStr, String timeoutStr, String thresholdStr) {
int port = NumberUtils.toInt(portStr, 8080); // Default to 8080
long timeout = NumberUtils.toLong(timeoutStr, 30000L); // Default to 30 seconds
double threshold = NumberUtils.toDouble(thresholdStr, 0.5); // Default to 0.5
return new Config(port, timeout, threshold);
}
public static void main(String[] args) {
// Valid configuration
Config config1 = parseConfig("9090", "60000", "0.75");
System.out.println(config1);
// Invalid configuration (uses defaults)
Config config2 = parseConfig("invalid", "not_a_number", "abc");
System.out.println(config2);
// Mixed configuration
Config config3 = parseConfig("8080", "invalid", "0.25");
System.out.println(config3);
}
}

Data Validation

public class DataValidator {
public static boolean isValidAge(String ageStr) {
if (!NumberUtils.isCreatable(ageStr)) {
return false;
}
int age = NumberUtils.toInt(ageStr, -1);
return NumberUtils.isInRange(age, 0, 150);
}
public static boolean isValidPercentage(String percentageStr) {
if (!NumberUtils.isCreatable(percentageStr)) {
return false;
}
double percentage = NumberUtils.toDouble(percentageStr, -1.0);
return NumberUtils.isInRange(percentage, 0.0, 100.0);
}
public static boolean isValidId(String idStr) {
return NumberUtils.isDigits(idStr) && NumberUtils.isPositive(NumberUtils.toLong(idStr, -1L));
}
public static void main(String[] args) {
System.out.println(isValidAge("25")); // true
System.out.println(isValidAge("200")); // false
System.out.println(isValidAge("abc")); // false
System.out.println(isValidPercentage("75.5")); // true
System.out.println(isValidPercentage("150")); // false
System.out.println(isValidPercentage("abc")); // false
System.out.println(isValidId("12345")); // true
System.out.println(isValidId("0")); // false
System.out.println(isValidId("abc")); // false
}
}

Financial Calculations

import java.math.BigDecimal;
import java.math.RoundingMode;
public class FinancialCalculator {
public static BigDecimal calculateInterest(BigDecimal principal, BigDecimal rate, int years) {
if (!NumberUtils.isPositive(principal) || !NumberUtils.isPositive(rate) || !NumberUtils.isPositive(years)) {
throw new IllegalArgumentException("All parameters must be positive");
}
BigDecimal multiplier = BigDecimal.ONE.add(rate.divide(BigDecimal.valueOf(100), 4, RoundingMode.HALF_UP));
BigDecimal result = principal.multiply(NumberUtils.pow(multiplier, years));
return result.setScale(2, RoundingMode.HALF_UP);
}
public static boolean isValidCurrencyAmount(String amountStr) {
if (!NumberUtils.isCreatable(amountStr)) {
return false;
}
double amount = NumberUtils.toDouble(amountStr, -1.0);
return NumberUtils.isPositive(amount) && NumberUtils.isFinite(amount);
}
public static void main(String[] args) {
BigDecimal principal = new BigDecimal("10000");
BigDecimal rate = new BigDecimal("5.5");
int years = 3;
BigDecimal interest = calculateInterest(principal, rate, years);
System.out.printf("Interest after %d years: $%.2f%n", years, interest);
System.out.println(isValidCurrencyAmount("1234.56")); // true
System.out.println(isValidCurrencyAmount("-100")); // false
System.out.println(isValidCurrencyAmount("abc")); // false
}
}

Performance Considerations

When to Use NumberUtils vs Standard Java Methods

public class PerformanceComparison {
public static void main(String[] args) {
// Use NumberUtils for null-safe operations
NumberUtils.max(10, null); // Safe
// Use NumberUtils for parsing with defaults
NumberUtils.toInt("123", -1); // Convenient
// Use standard Java methods for simple operations
Math.max(10, 20); // More efficient for non-null values
// Use NumberUtils for validation
NumberUtils.isCreatable("123.45"); // More comprehensive than manual regex
}
}

Best Practices

  1. Always use null-safe methods when dealing with user input or external data
  2. Use isCreatable() for validation before parsing strings to numbers
  3. Provide default values when parsing to handle invalid input gracefully
  4. Use appropriate numeric types for your use case (int, long, double, BigDecimal)
  5. Consider precision when working with floating-point numbers
  6. Validate ranges for business logic constraints

Common Pitfalls

public class CommonPitfalls {
public static void main(String[] args) {
// Pitfall 1: Not checking if string is creatable
String invalidNumber = "abc";
// Integer.parseInt(invalidNumber); // Throws NumberFormatException
NumberUtils.toInt(invalidNumber, -1); // Safe, returns -1
// Pitfall 2: Not handling null values
Integer num = null;
// Math.max(10, num); // Throws NullPointerException
NumberUtils.max(10, num); // Safe, returns 10
// Pitfall 3: Not validating ranges
String ageStr = "200";
if (NumberUtils.isCreatable(ageStr)) {
int age = NumberUtils.toInt(ageStr, -1);
if (!NumberUtils.isInRange(age, 0, 150)) {
// Handle invalid age range
}
}
// Pitfall 4: Not considering precision for financial calculations
double result = 0.1 + 0.2;
System.out.println(result); // 0.30000000000000004 (not 0.3)
// Use BigDecimal for financial calculations
}
}

Conclusion

NumberUtils from Apache Commons Lang provides a robust set of utilities that make number operations safer, more convenient, and more powerful. By understanding these methods and their proper usage, you can write cleaner, more maintainable code that handles edge cases gracefully.

The key benefits include:

Start incorporating NumberUtils into your projects and see how it simplifies your number processing code!

Resources

Written by Purusothaman Ramanujam

← Back to blog