I was just wondering if there's a more efficient way to do something along the lines of this in Java.
Just going to use this dummy function as an example
static int getDivision(int number, int divider) {
if(divider == 0) { return -1; }
return (number / divider);
}
If I have a ternary operator that is checking the result of that function like the below
public static void main(String[] args) {
int result;
result = getDivision(2, 0);
System.out.println (
result == -1 ? "No" : result
);
}
I have to create a variable to store the result, otherwise I can do it like this
System.out.println (
getDivision(2, 0) == -1 ? "No" : getDivision(2, 0)
);
But I have to call the function twice which is even worse. Is it possible to use the ternary operator but include the result of a function in the conditions?
System.out.println (
getDivision(2, 0) == -1 ? "No" : /*returned result*/
);
Using Optional, you can represent a real result as well as indicate that no result is present:
static Optional<Integer> getDivision(int number, int divider) {
return divider == 0 ? Optional.empty() : Optional.of(number / divider);
}
Usage:
Optional<Integer> result = getDivision(2, 0);
System.out.println(result.map(String::valueOf).orElse("No"));
Or if you only want to further process the result when it is present:
result.ifPresent( value -> ...);
EDIT: the value -1 is also a valid outcome of some divisions, which works fine with the Optional as well, compared to the original approach:
Optional<Integer> result = getDivision(-5, 5);
Related
I'm trying to create Predicates to test Strings and determine the most appropriate variable type, which means I want to get the "smallest" numeric variable type that the parsed String fits into.
So for example, for a String "-1.2" the detected type should be float, but if the number exceeds the float range, it should detect double. If the number is even bigger, then BigDecimal.
For non-floating point numbers my tests seem to work fine,
but the decimal numbers are giving me more headaches than they should.
This is my isFloat test:
Predicate<String> isFloat =
s -> {
try {
Double d = Double.parseDouble(s);
return d >= -Float.MAX_VALUE && d <= Float.MAX_VALUE;
} catch (Exception e) {
return false;
}
};
String minOfFloat = String.valueOf(-Float.MAX_VALUE);
System.out.println(minOfFloat);
System.out.println(Double.parseDouble(minOfFloat));
System.out.println(isFloat.test(minOfFloat));
Outputs:
-3.4028235E38
-3.4028235E38
false //WHYYYYY
Similarly my next test would be to check if the number fits a double, and if not, return "BigDecimal".
What's the correct way to test this?
EDIT: This is the way I'm doing it, accepted answer brought me there:
public static final Predicate<String> isFloat =
s -> {
try {
Double d = Double.parseDouble(s);
return d.floatValue() >= -Float.MAX_VALUE
&& d.floatValue() <= Float.MAX_VALUE
&& !Float.isInfinite(d.floatValue())
&& !Float.isNaN(d.floatValue());
} catch (Exception e) {
return false;
}
};
Change your predicate to compare with d.floatValue() >= -Float.MAX_VALUE && d <= Float.MAX_VALUE;
In case, if your double value exceeds float range d.floatValue() returns infinity so you should be good with the change
Predicate<String> isFloat = s -> {
try {
Double d = Double.parseDouble(s);
return d.floatValue() >= -Float.MAX_VALUE && d <= Float.MAX_VALUE;
} catch (Exception e) {
return false;
}
};
String maxDouble = String.valueOf(Double.MAX_VALUE);
System.out.println(maxDouble);
System.out.println(isFloat.test(maxDouble));
Using BigDecimal is recomended when widening primitive conversion will be applied. I couldn't find a good duplicate, but see this posts
how-to-compare-correctly-long-and-float-primitives
comparing-float-and-double-primitives-in-java
to understand why you might get wrong results when comparing two different types. So I would do something like:
Predicate<String> isFloat =
str -> new BigDecimal(str).compareTo(new BigDecimal(String.valueOf(Float.MAX_VALUE))) <= 0 &&
new BigDecimal(str).compareTo(new BigDecimal(String.valueOf(-Float.MAX_VALUE))) >= 0;
Something fundamental about hashmaps and for loops is hard for me to grasp here. What I'm trying to do is add +1 to the value associated with the key based on the Keys method every time the value within the array list is associated with the key string.
So if there are 3 values in the array list that are positive then the hashmap should have the value with the key "positive" updated to 3.
Any help/advice would be appreciated - thank you.
public String Keys(double input){
if (input > 0){
System.out.println("positive");
}
else if (input < 0) {
System.out.println("negative");
}
else if (input == 0) {
System.out.println("zero");
}
return "";
}
public HashMap<String, Integer> increaseValues(ArrayList<Double> inputs){
HashMap<String, Integer> hashMap = new HashMap<>();
hashMap.put("positive", 0);
hashMap.put("negative", 0);
hashMap.put("zero", 0);
//What I tried before adding the Keys method.
//This updates the value but the loop won't continue if another input in the
//arraylist is true.
for (int i = 0; i < inputs.size(); i++){
double input = inputs.get(i);
if (input > 0){
hashMap.put("positive", 1);
} else if (input < 0){
hashMap.put("negative", 1);
} else if (input == 0){
hashMap.put("zero", 1); }
return hashMap;
}
public void main(String[] args){
ArrayList<Double> inputs = new ArrayList<>();
inputs.add(-4.56);
inputs.add(-4.66);
inputs.add(0.0);
inputs.add(6.0);
inputs.add(-6.99);
inputs.add(6.97);
}
Map.put(k, v) always overrides your previous value. You can use the "traditional approach":
if (!map.containsKey("positive"))
map.put("positive", 0);
map.put("positive", map.get("positive") + 1);
or better use the new merge funtction that is added for exactly such cases:
map.merge("positive", 1, (prev, one) -> prev + one);
But this whole logic can be greatly shortened by using Math.signum() and stream collectors:
Map<Double, Long> collect = inputs.stream()
.collect(Collectors.groupingBy(Math::signum,
Collectors.counting()));
System.out.println("positive: " + collect.get(1.0));
System.out.println("negative: " + collect.get(-1.0));
System.out.println("zero: " + collect.get(0.0));
Lets start with a quick tidy, lets create an enum and remove those nasty String constants that you don't have defined anywhere:
public enum Sign {
POSITIVE,
ZERO,
NEGATIVE;
public static Sign of(double d) {
if (d > 0) {
return POSITIVE;
}
if (d < 0) {
return NEGATIVE;
}
return ZERO;
}
}
Now we can trivially write a method to increment the appropriate value:
public void increment(final double d, final Map<Sign, Integer> count) {
count.merge(Sign.of(d), 1, Integer::sum);
}
For a quick test:
final Map<Sign, Integer> count = new EnumMap<>(Sign.class);
increment(0, count);
System.out.println(count);
increment(-1, count);
System.out.println(count);
increment(1, count);
System.out.println(count);
increment(-2, count);
System.out.println(count);
increment(2, count);
System.out.println(count);
Output:
{ZERO=1}
{NEGATIVE=1, ZERO=1}
{POSITIVE=1, NEGATIVE=1, ZERO=1}
{POSITIVE=1, NEGATIVE=2, ZERO=1}
{POSITIVE=2, NEGATIVE=2, ZERO=1}
So how does this magic work? From the documentation for Map.merge
If the specified key is not already associated with a value or is
associated with null, associates it with the given non-null value.
Otherwise, replaces the associated value with the results of the given
remapping function, or removes if the result is null. This method may
be of use when combining multiple mapped values for a key.
So it takes the key as the first argument to merge - in the case Sign.of(d); this selects the correct bucket. If the value mapped to that key is null then it simply puts a mapping for the key to the value passed as the second argument - in this case 1. Otherwise it gets a litte more complicated; it takes the value currently mapped to that key and uses the remappingFunction passed as the third argument. This is a BiFunction<V,V,V>, so it takes two arguments of type V, the value type and returns a single one - it merges the two together. Here we use Integer::sum to take the existing value, the new value and return their sum.
But we can go one step further, we can use a Stream to carry this out on an arbitrary double[]:
public Map<Sign, Long> count(final double[] d) {
return Arrays.stream(d)
.mapToObj(Sign::of)
.collect(groupingBy(identity(), () -> new EnumMap<>(Sign.class), counting()));
}
Note: I've used an EnumMap here, which is Map optimised for using an enum as the key.
You can solve it pretty concisely with streams. You need a function which turns value into the negative/zero/positive key. Then just group by this key with a counting collector. It's basically a two-liner:
List<Double> values = Arrays.asList(-4.56,-4.66,0.0, 6.0, -6.99, 6.97);
Function<Double, String> toKey = v -> v < 0 ? "negative" : (v == 0 ? "zero" : "positive");
Map<String, Long> result = values
.stream()
.collect(Collectors
.groupingBy(
toKey,
Collectors.counting()));
The method put of HashMap maps unique keys with values. It replaces the existing value with the new value in case the provided key is already present in the map.
You need to define a method similar to below.
public void addValueToList(Double d, List<Double> inputs , Map<String,Integer> map){
if( d == null ) return;
if ( isZero(d) ){
Integer count = map.get("zero");
if(count == null){
count = 1;
}else {
count ++;
}
map.put("zero",count);
}else if( isNegative(d) ) {
Integer count = map.get("negative");
if(count == null){
count = 1;
}else {
count ++;
}
map.put("negative",count);
}else {
Integer count = map.get("positive");
if(count == null){
count = 1;
}else {
count ++;
}
map.put("positive",count);
}
inputs.add(d);
}
For writing method for comparing the value you can refer https://stackoverflow.com/a/10400718/504133
I want to display an error if more than one of the four variables is set...
In Java..this is what I came up with..
if( (isAset() && isBset()) || (isBset() && isCset()) || (isCset() && isDset()) || (isDset() && isAset()) )
attri.greedySelectionException(..);
I wanted to check if there is a better way of doing this..?
How about you use a counter and then compare it to 1?
Something like...
int i = 0;
if (isAset()) i++;
if (isBset()) i++;
if (isCset()) i++;
if (isDset()) i++;
if (i > 1)
...
Alternatively, if you are checking properties of a certain object, you could use some reflection to iterate through the relevant properties instead of having one if statement per property.
Edit: Take a look at Marius Žilėnas's varargs static method below for some tidier code, i.e. using (changed the oldschool for to a for-each and the ternary expression for an if):
static int trueCount(boolean... booleans) {
int sum = 0;
for (boolean b : booleans) {
if (b) {
sum++;
}
}
return sum;
}
instead of several if statements.
You can simplify this expression with :
if((isAset() || isCset()) && (isBset() || isDset()))
attri.greedySelectionException(..);
Wolfram alpha made the work for you :
Original expression
You can verify with the truth tables :
Original
Final
In Java 8 you can solve this problem with Streams in an elegant way (assuming your values are null if they are not set):
if (Stream.of(valueA, valueB, valueC, valueD).filter(Objects::nonNull).count() != 1) {
/* throw error */
}
If you have control on the implementation of isAset(), isBSet, isCSet, & isDset methods, you can achieve this with much more clarity if you return 1 or 0 instead of true or fales from this functions. These functions are to be created as below...
public int isAset()
{
return (A != null) ? 1 : 0;
}
To verify if more than one variable is set use use something like below...
if( isASet() + isBSet() + isCSet() + isDSet() > 1)
ThrowMoreAreSetException()
If you don't have control on this here is another way of doing it...
int count = isASet() ? 1 : 0;
count+= isBSet() ? 1 : 0;
count+= isCSet() ? 1 : 0;
count+= isDSet() ? 1 : 0;
if(count > 1)
ThrowMoreAreSetException()
By following either of these approches, code will be less clumsy and more readable than doing somany comparision combinations.
I suggest using varargs ... (see Java tutorial) and make a function that calculates how many trues was given. The following code to demonstrates it:
public class Values
{
public static boolean isASet()
{
return false;
}
public static boolean isBSet()
{
return true;
}
public static boolean isCSet()
{
return true;
}
public static boolean isDSet()
{
return true;
}
public static int booleans(boolean... booleans)
{
int sum = 0;
for (int i = 0; i < booleans.length; i++)
{
sum += booleans[i] ? 1 : 0;
}
return sum;
}
public static void main(String[] args)
{
System.out.println(
booleans(isASet(), isBSet(), isCSet(), isDSet()));
if (1 < booleans(isASet(), isBSet(), isCSet(), isDSet()))
{
System.out.println("Condition met.");
}
}
}
Try this:
if (!(isAset ^ isBset ^ isCset ^ isDset))
This will true only is any one is true or else false.
What is wrong with the below code? It will throw NullPointerException while execution time.
public class Test
{
public String method1()
{
return null;
}
public Integer method2()
{
return null;
}
public static void main(String args[])throws Exception
{
Test m1 = new Test();
Integer v1 = (m1.method1() == null) ? m1.method2() : Integer.parseInt(m1.method1());
}
}
The type of a a ? b : c is the type of the last value c. In this case it's an int. This means that even though b is being chosen it is being unboxed and then re-boxed into an Integer. As the value is null, this fails.
Here is a similar example which may help (or be more confusing)
Integer i = 1000;
// same as Integer j = Integer.valueOf(i == 1000 ? i.intValue() : 1000);
Integer j = i == 1000 ? i : 1000;
System.out.println(i == j);
Integer k = i == 1000 ? i : (Integer) 1000;
System.out.println(i == k);
prints
false
true
The reason the first result is false, is that expression has a type of int (the last argument) which means i is unboxed to be an int and reboxed so it can be assigned to the Integer. This results in a different object (There are command line args which would increase the cache size and change this) In the second example the type is Integer so it is not unboxed and the object is the same.
parseInt returns int. That makes the compiler to unbox m1.method2() but it is null so it throws:
Integer v1 = (m1.method1() == null) ? m1.method2() : (Integer)Integer.parseInt(m1.method1());
In a column "Size", I have weights like 100mL , 0.5mg, 1L, 2500cm2. I need to sort it out according to ml, mg, gram, litre, cm etc. Please give me solution for it, I have tried with String Comparison but its not worthable as we have to consider weights.
public static int safeCompareIgnoreCase(String name1, String name2) {
if (name1 == name2) {
return 0;
}
if (name2 == null) {
return -1;
}
if (name1 == null) {
return 1;
}
return name1.compareToIgnoreCase(name2);
}
public int compare(InventorySearchResultRecord r1, InventorySearchResultRecord r2) {
int result = safeCompareIgnoreCase(r1.getSize(), r2.getSize());
if (result != 0) {
return result;
}
}
Hint: - You should not compare two values in different units.. Think yourself -> How will you tell whether 1 Dollar is greater or 50 Rupees?? You need to convert them into same units..
And first try to do it on paper.. Get your work done without computer.. If you got the answer, convert it to code.. It would be easier for you..