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
- Always use null-safe methods when dealing with user input or external data
- Use
isCreatable()for validation before parsing strings to numbers - Provide default values when parsing to handle invalid input gracefully
- Use appropriate numeric types for your use case (int, long, double, BigDecimal)
- Consider precision when working with floating-point numbers
- 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:
- Null safety - No more NullPointerExceptions
- Default values - Graceful handling of invalid input
- Comprehensive validation - Easy checking of number formats and ranges
- Type conversion - Safe conversion between numeric types
- Mathematical operations - Convenient math utilities
Start incorporating NumberUtils into your projects and see how it simplifies your number processing code!
Resources
Written by Purusothaman Ramanujam
← Back to blog