I don't understand how to use lambda expressions - java

I'm trying to get a lambda expression to count the amount of odd numbers in a small collection of integers. In general I'm just confused about the syntax and the types I can use with them. So far I have:
Collection<Integer> col = Arrays.asList(1, 2, 3, 4);
int count = 0;
count = col.forEach((Integer n) -> { if ((n % 2) != 0) count++;});
It says it can't convert from void to int, no idea why that's the error that comes up.

You can't modify local variables from within a lambda. You can use a mutable reference, but it would be much simpler to go with streams:
long count = col.stream().filter(n -> n % 2 != 0).count();

The variable count has type int. The lambda expression with forEach has type void, it's just like running the for loop, it doesn't return anything. So, your assignment
count = col.forEach(...)
is actually trying to assign void to int.

Related

How to create a variable that contains an Operator in Java?

I'm trying to write a program in 30 lines or less for my class (self imposed challenge).
The program asks the user a simple addition, division, multiplication, or subtraction question and the player answers, rinse and repeat 10 times, then the player is asked if they want to continue or end the program. The type of question (add, mult, etc.) should be selected randomly.
So that I don't need to use a huge switch case or if-else tree, I want to know if there is any way to contain an operator in a variable, and then use it later.
Example:
var operator = +;
int[] n = {1, 2};
System.out.println(n[0] operator n[1]);
And the output would be "3"
That's an example of what kinda thing I want to do. Is this possible?
No, you can't do that directly. You will have to make a type that represents an operator.
The most common way to do this is with an enum:
enum Operator {
PLUS {
#Override int operate(int a, int b) {
return a + b;
}
};
abstract int operate(int a, int b);
}
Operator operator = Operator.PLUS;
int[] n = {1, 2};
System.out.println(operator.operate(n[0], n[1]));
It's not possible to assign an operator to a variable.
in 30 lines or less ... the program asks the user a simple addition, division, multiplication, or subtraction
If you want to implement it using as fewer lines as possible, build-in functional interfaces will be a good choice. For that, you need IntBinaryOperator that represents an operation done on two int arguments.
Functional interface can be implemented either by using a lambda expression or a method reference (also, you can do that with an anonymous inner class as well by it'll not be shorter). Addition operation can be represented like that:
IntBinaryOperator add = Integer::sum; // or (i1, i2) -> i1 + i2
The type of question (add, mult, etc.) should be selected randomly
For that, firstly, you need to define a Random object. In order to obtain a random integer in the given range, use nextInt() method that expects an int bound, and returns a value from 0 up to the bound (exclusive):
rand.nextInt(RANGE)
To avoid hard-coding, RANGE should be defined as a global constant.
Because your application has to interact with the user, every operation should be associated with a name that will be exposed to the user.
It can be done by declaring a record (a special kind class with final field and auto-generated constructor, getters, hashCode/equals, toString()). Syntax for declaring records is very concise:
public record Operation(String name, IntBinaryOperator operation) {}
Records representing arithmetical operations can be stored in a list. And you can pick an operation by generating a random index (from 0 up to list size).
operations.get(rand.nextInt(operations.size()))
Unlike the common getters, names of getters that will be generated by the compiler for the record will identical to names of its fields, i.e. name() and operation().
In order to use the function retrieved from the record, you need to invoke the method applyAsInt() on it, passing the two previously generated numbers.
That's how it might look like.
public class Operations {
public static final int RANGE = 100;
public static final Random rand = new Random();
public record Operation(String name, IntBinaryOperator operation) {}
public static final List<Operation> operations =
List.of(new Operation("add", Integer::sum), new Operation("sub", (i1, i2) -> i1 - i2),
new Operation("mult", (i1, i2) -> i1 * i2), new Operation("div", (i1, i2) -> i1 / i2));
public static void main(String[] args) {
// you code (instansiate a scanner, enclose the code below with a while loop)
for (int i = 0; i < 10; i++) {
Operation oper = operations.get(rand.nextInt(operations.size()));
int operand1 = rand.nextInt(RANGE);
int operand2 = rand.nextInt(RANGE);
System.out.println(operand1 + " " + oper.name() + " " + operand2); // exposing generated data to the user
int userInput = sc.nextInt(); // reading user's input
int result = oper.operation().applyAsInt(operand1,operand2); // exposing the result
System.out.println(result + "\n__________________");
}
// termination condition of the while loop
}
}
That's an example of the output the user will see:
38 add 67
105 // user input
105
_____________________
97 sub 15
...
Unfortunately java supports neither operator overloading nor infix notation, making it impossible to do so in source code.

Finding common element in two arrays with best performing method

Implement a method that checks whether an integer is present in both integer array parameter 1 and integer array parameter 2 and prints the result of the search, with the best performance you can. The method parameters are: (1) the first integer array and (2) the second integer array of the same size as parameter 1 and (3) the integer to search for.
Note - Consider better performance to mean that a better performing method requires fewer general work steps to solve the problem with the same size of arrays. You may want to review the Java SE API page for java.util.Arrays
I was able to implement the solution but I am not sure if it the best-performing one because I am not using any java.util.Arrays methods as I am not sure which one to use necessarily to get me the best answer
public static void findCommonElements(int[] arr1, int[] arr2, int num){
for(int i = 0; i < arr1.length; i++){
for(int j = 0; j < arr2.length; j++){
if(arr1[i] == arr2[j] && arr1[i] == num){
System.out.println(num);
}
}
}
}
UPDATE:
I was able to update the code with following solution which completely removes for loop and implements binary for better performance
int[] arr1 = {7,8,5,1,2,3,6,7};
int[] arr2 = {9,8,6,4,1,2,4,5};
Arrays.sort(arr1);
Arrays.sort(arr2);
int index1 = Arrays.binarySearch(arr1, 5);
int index2 = Arrays.binarySearch(arr2, 5);
System.out.println(index1);
System.out.println(index2);
if(index1 < 0 || index2 < 0){
System.out.println("number not found in both arrays");
}
else{
System.out.println("number found in both arrays");
}
The problem description is a bit hard to follow, but by reference to the example code, I take this to be a fair rewording: "Write the best-performing method you can that takes two int arrays of the same length and a scalar int value i as parameters, and prints whether the value of i appears in both arrays."
Your first solution tests each pair of elements drawn one from the first array and the other from the second to determine whether they are equal to each other and to the target value. This is grossly inefficient for the problem as interpreted.
Your second solution sorts the arrays first, so as to be able to use a binary search to try to find the target element. This is better, but still inefficient. Although the binary searches are quite fast, the sorting required to prepare for them takes a lot more work than is saved by a single binary search.
Since it is sufficient to determine only whether the target value appears in both arrays, you can
scan each array for the target value, once, independently of the other.
skip the second scan if the first one does not find the target value
break early from each scan when the target value is found
The latter two are minor improvements, as they reduce only the minimum and average number of steps. The first, however, is a huge improvement, especially as array size increases, because for arrays of length n, then this requires a number of steps proportional to n in the worst case, whereas your first example code requires steps proportional to n2 in both the average and worst cases, and your second requires time proportional to n log n in the average and worst cases.
The implementation is left as the exercise it is intended to be. However, with respect to
I was able to implement the solution but I am not sure if it the
best-performing one because I am not using any java.util.Arrays
methods as I am not sure which one to use necessarily to get me the
best answer
, I don't think java.util.Arrays offers any method that particularly helps with this problem, especially given the orientation toward best possible performance.
You can use search the arrays using streams:
public static boolean findCommonElements(int[] arr1, int[] arr2, int num) {
return Arrays.stream(arr1).anyMatch(x -> x == num) &&
Arrays.stream(arr2).anyMatch(x -> x == num);
}
Similar method using linear search in arrays of Integer using Arrays.asList to convert arrays:
public static boolean findCommonElements(Integer[] arr1, Integer[] arr2, int num) {
return Arrays.asList(arr1).indexOf(num) > -1 &&
Arrays.asList(arr2).indexOf(num) > -1;
}

Count Elements in a stream and return Integer insted of long

I need to count Elements in a Stream and assign it to an Integer without casting.
.count() does return long
thought about the .collect(Collectors.reducing(..)) but cant figure it out.
I feel like there is something simple I don't get.
My Try:
Stream<String> s = Stream.of("Hallo ", "Test", "String");
Integer count = s.filter(e -> (e.length() >= lb && e.length() <= ub && !e.contains(" ")))
.map(e -> e.toUpperCase())
.distinct()
.collect(Collectors.reducing(0, e -> 1, Integer::sum)));
System.out.println(count);
Simply: don't.
Don't cast, but also don't make things overly complicated.
Rather look into safe ways of getting that int out of the long returned by count(). See here for starters:
int bar = Math.toIntExact(someLong);
for example. When you are 100% sure that the computed value always fits within int, then you just avoid putting down the catch for the potentially thrown ArithmeticException. And you still got that good feeling that you can't "overrun" without noticing.
But as said: don't invest time/energy into specially computing your own stuff, when you can use built-in functionality to count things, and turn them into int/Integer. Remember: each character you put into code needs to be read and understood later on. Thus even "20 characters" more add up over time. So when you always lean towards the shorter solution, as long as they are easy to read/understand.
Here is the right way. Convert all the distinct values to 1 using Stream::mapToInt - it produces the IntStream which has sum/count methods able to handle stream of numeric values directly without mapping:
Integer count = s.filter(e -> (e.length() >= lb && e.length() <= ub && !e.contains(" ")))
.map(String::toUpperCase)
.distinct()
.mapToInt(i -> 1)
.sum();
Without mapping to int, you can use Stream::reduce(U identity, BiFunction<U,? super T,U> accumulator, BinaryOperator<U> combiner) to get the very same result:
Integer count = s.filter(e -> (e.length() >= 2 && e.length() <= 10 && !e.contains(" ")))
.map(String::toUpperCase)
.distinct()
.reduce(0, (a,b) -> a + 1, (a,b) -> a + b);
The interface of this method is little bit complicated:
U identity is set to 0 - a start of counting
accumulator ((a,b) -> a + 1) converts the String to int, each String will be converted to 1 and added to the previous result (0+1+1+1...).
combiner combines two consecutive values ((a,b) -> a + b) - the sum of the 1 values, which is practically the count.
If you want to count the elements in stream without using the build in .count() method then you could map each element to an int and reduce by summing them. Something like this:
Integer count = s.mapToInt(i -> 1).reduce((a, b) -> a + b).orElse(0);
Or as #Holger commented bellow to use the sum() after mapping.
Integer count = s.mapToInt(i -> 1).sum();
With Java 8, you can use Math.toIntExact(long val).
public static int toIntExact(long value)
Returns the value of the long argument; throwing an exception if the
value overflows an int.

Java 8 Stream and operation on arrays

I have just discovered the new Java 8 stream capabilities. Coming from Python, I was wondering if there was now a neat way to do operations on arrays like summing, multiplying two arrays in a "one line pythonic" way ?
Thanks
There are new methods added to java.util.Arrays to convert an array into a Java 8 stream which can then be used for summing etc.
int sum = Arrays.stream(myIntArray).sum();
Multiplying two arrays is a little more difficult because I can't think of a way to get the value AND the index at the same time as a Stream operation. This means you probably have to stream over the indexes of the array.
//in this example a[] and b[] are same length
int[] a = ...
int[] b = ...
int[] result = new int[a.length];
IntStream.range(0, a.length).forEach(i -> result[i] = a[i] * b[i]);
Commenter #Holger points out you can use the map method instead of forEach like this:
int[] result = IntStream.range(0, a.length).map(i -> a[i] * b[i]).toArray();
You can turn an array into a stream by using Arrays.stream():
int[] ns = new int[] {1,2,3,4,5};
Arrays.stream(ns);
Once you've got your stream, you can use any of the methods described in the documentation, like sum() or whatever. You can map or filter like in Python by calling the relevant stream methods with a Lambda function:
Arrays.stream(ns).map(n -> n * 2);
Arrays.stream(ns).filter(n -> n % 4 == 0);
Once you're done modifying your stream, you then call toArray() to convert it back into an array to use elsewhere:
int[] ns = new int[] {1,2,3,4,5};
int[] ms = Arrays.stream(ns).map(n -> n * 2).filter(n -> n % 4 == 0).toArray();
Be careful if you have to deal with large numbers.
int[] arr = new int[]{Integer.MIN_VALUE, Integer.MIN_VALUE};
long sum = Arrays.stream(arr).sum(); // Wrong: sum == 0
The sum above is not 2 * Integer.MIN_VALUE.
You need to do this in this case.
long sum = Arrays.stream(arr).mapToLong(Long::valueOf).sum(); // Correct
Please note that Arrays.stream(arr) create a LongStream (or IntStream, ...) instead of Stream so the map function cannot be used to modify the type. This is why .mapToLong, mapToObject, ... functions are provided.
Take a look at why-cant-i-map-integers-to-strings-when-streaming-from-an-array

Mod division of two integers

I keep getting the error "The operator % is undefined for the argument type(s) Integer, Integer" I am not quite sure why this is happening. I thought that since modular division cannot return decimals that having integer values would be alright.
This is happening within a method in a program I am creating.
The code is as follows:
public void addToTable(Integer key, String value)
{
Entry<Integer, String> node = new Entry<Integer, String>(key, value);
if(table[key % tableSize] == null)
table[key % tableSize] = node;
}
The method is unfinished but the error occurs at
if(table[key % tableSize] == null)
and
table[key % tableSize] = node;
any help or suggestions would be appreciated.
I could get some sample Integer % Integer code to compile successfully in Java 1.5 and 1.6, but not in 1.4.
public static void main(String[] args)
{
Integer x = 10;
Integer y = 3;
System.out.println(x % y);
}
This is the error in 1.4:
ModTest.java:7: operator % cannot be applied to java.lang.Integer,java.lang.Integer
System.out.println(x % y);
^
The most reasonable explanation is that because Java introduced autoboxing and autounboxing in 1.5, you must be using a Java compiler from before 1.5, say, 1.4.
Solutions:
Upgrade to Java 1.5/1.6/1.7.
If you must use 1.4, use Integer.intValue() to extract the int
values, on which you can use the % operator.
This works fine for me.
Integer x = Integer.valueOf(10);
Integer y = Integer.valueOf(3);
int z = x % y;
System.out.println(z);
No problems. Output:
1
What error are you getting? What version of Java are you using? It seems that you're using Java below 1.5.
What you're attempting here is called unboxing, the auto-conversion of an object to a primitive type (going the other way is autoboxing).
The Java docs have this to say:
The Java compiler applies unboxing when an object of a wrapper class is:
Passed as a parameter to a method that expects a value of the corresponding primitive type.
Assigned to a variable of the corresponding primitive type.
So one possibility is that you're not doing one of those things and, although it appears at first glance that you're neither passing your mod expression to a method nor assigning it to a variable, it's valid, at least in Java 6:
class Test {
public static void main(String args[]) {
Integer x = 17;
Integer y = 5;
System.out.println (x % y);
String [] z = new String[10];
z[x % y] = "hello";
}
}
The other possibility is that you're using a pre Java 5 environment, where autoboxing and unboxing was introduced.
Best bet in that case is probably to be explicit and use Integer.intValue() to get at the underlying int.
However you may also want to consider using an int (not Integer) for the key and only boxing it up at the point where you need to (when you add it to an Entry). It may well be faster to use the primitive type, though you should of course benchmark it to be sure.
Try converting the Integers to ints, then run %.
if(table[key.intValue() % tableSize.intValue()] == null)
table[key.intValue() % tableSize.intValue()] = node;

Categories