 Java, Spring and Web Development tutorials  1. Overview
When working with numbers, it is sometimes necessary to have a dedicated value for an error condition or nonexistent values. If we use wrapper classes, the task is easy; we can set it to null, which might not be the best practice, but at least it does the job. For non-numeric values and the classes we can extend, the task is even more straightforward, and we can have a cleaner approach by creating a dedicated class to represent an error condition.
However, in this tutorial, we’ll talk about how to achieve the same for primitive values. There are a few cases where we might need it, and it’s not obvious what values we can use. We consider a few options, making it easier to select the most appropriate one for a given case. We also explore a few approaches that eliminate the need for setting non-values entirely, which isn’t directly connected to the topic but might be useful and provide a different perspective on the problem.
2. Throwing an Exception
The easiest way to handle incorrect values is to prevent them from being processed in the first place. For example, if we attempt to read the data about employees and some of them don’t have an age, or the age is set to zero, we can stop the processing at that point:
public static double findLargestThrowException(double[] array) {
if (array.length == 0) {
throw new IllegalArgumentException("Array cannot be empty");
}
double max = array[0];
for (int i = 1; i < array.length; i++) {
if (array[i] > max) {
max = array[i];
}
}
return max;
}
This approach would maintain the invariants of our system and simplify it because if we don’t allow garbage in, we don’t have to handle it in other parts of our application.
3. Ignoring Incorrect Values
Another approach, similar to the previous one, is to skip the values that would result in incorrect calculations or cause the system to break. This is not always possible, but it’s a reasonable approach:
public static double findLargestIgnoreNonValues(double[] array) {
double max = Double.NEGATIVE_INFINITY;
boolean foundValidValue = false;
for (double value : array) {
if (!Double.isNaN(value) && !Double.isInfinite(value)) {
if (!foundValidValue || value > max) {
max = value;
foundValidValue = true;
}
}
}
return foundValidValue ? max : -1.0;
}
For instance, if we need to calculate the average rainfall for a specific region and some sensors are malfunctioning or missing data, we can still obtain the most precise calculations possible based on the available information. However, as for the previous approach, it’s not always possible.
4. The Value Outside of The Allowed Range
This solution heavily depends on the domain we’re working with. In some cases, we can represent “non-values” by values outside the allowed range. It’s heavily used in the standard libraries, for example, if we use indexOf(value), if we cannot find the given element, the result we get is negative:
public static double findLargestReturnNegativeOne(double[] array) {
if (array.length == 0) {
return -1.0;
}
double max = Double.NEGATIVE_INFINITY;
boolean foundValidValue = false;
for (double value : array) {
if (!Double.isNaN(value) && !Double.isInfinite(value)) {
if (!foundValidValue || value > max) {
max = value;
foundValidValue = true;
}
}
}
return foundValidValue ? max : -1.0;
}
The code won’t throw an exception or return a result; it would still be syntactically and semantically correct. However, the answer won’t make sense as we cannot get the element with a negative index.
5. Using Wrappers
As mentioned in the introduction, objects, and in this case, wrapper classes, have a helpful feature: we can use null values for them. In this case, we don’t even need to change the signature of the method; we can change the return type:
public static Double findLargestWithWrapper(double[] array) {
Double max = null;
for (double value : array) {
if (!Double.isNaN(value) && !Double.isInfinite(value)) {
if (max == null || value > max) {
max = value;
}
}
}
return max;
}
However, this approach would lead us to the first two. In this case, we move the problem up the call stack and suggest that the client handle it for us, which isn’t the best practice.
6. Using Double.NaN
Another option, similar to the previous one, is to return a Double.NaN instead of null. This makes the code semantically more sound, but we still move the problem up on the call stack:
public static double findLargestReturnNaN(double[] array) {
double max = Double.NEGATIVE_INFINITY;
boolean foundValidValue = false;
for (double value : array) {
if (!Double.isNaN(value) && !Double.isInfinite(value)) {
if (!foundValidValue || value > max) {
max = value;
foundValidValue = true;
}
}
}
return foundValidValue ? max : Double.NaN;
}
One interesting thing about Double.NaN: when comparing two NaN values, it always returns false. Thus, if we rely on the comparison between the results, we should always use Double.isNaN(value).
7. Conclusion
In this article, we discussed how handling incorrect values or “non-values” is a tricky art and heavily depends on what we plan to do with the numbers. As always, throwing an exception is the best practice to prevent any garbage from entering our code. However, if this is not possible, we have a few options available. The “best” approach depends on the domain and the context where the code would run. Our goal is to select the most suitable choice.
As always, the code examples are available over on GitHub. The post Handling “non-value” doubles in Java first appeared on Baeldung.
Content mobilized by FeedBlitz RSS Services, the premium FeedBurner alternative. |