lambda retrieve integer from map - java

I am trying to get the number after doing calculations using Lambda expression, but getting the error.
The lambda expression I am using:
int num = Optional.ofNullable(list.stream().filter(x->x.getType().getTypeId()==Type.getTypeId()).limit(1).map(x->x.getNum())).get();
After filtering, I want to get the first retrieved value. But I am getting the error as
cannot convert from Stream<Integer> to int
So, currently the way I am using is
Optional<> li = list.stream().filter(x->x.getType().getTypeId()==Type.getTypeId()).findFirst();
if (li.isPresent()) {
num = li.map(x-> x.getNum()).get();
}
But, I was looking if the above could be done in a single line rather than extra if statement
Earlier, I tried the get() with findFirst(), but it was giving nullpointerException. How can I safely retrieve the value.

list.stream().filter(x->x.getType().getTypeId()==Type.getTypeId()).limit(1).map(x->x.getNum()) returns a Stream. You are lacking the findFirst terminal operation:
int num =
list.stream()
.filter(x->x.getType().getTypeId()==Type.getTypeId())
.map(x->x.getNum())
.findFirst()
.orElse(0); // default value in case the Stream is empty after the filtering

Related

Printing max element in a Deque<Integer>

I have a Deque from Integers, which I'm using as a Que. I have to add and remove elements from it and in some cases I need to print the max value in the Que.
The thing that I've tried is:
System.out.println(deque.stream().max(Integer::compareTo));
But it prints - Optional[the current max int].
Thank you!
That is because the max method of java.util.Stream returns your value wrapped in Optional. This is the signature of the max method Optional<T> max(Comparator<? super T> comparator);
The example below would provide you the expected behaviour:
Optional<Integer> optionalOfMax = deque.stream().max(Integer::compareTo);
System.out.println(optionalOfMax.orElseThrow());
You can do it as follows:
deque.stream().max(Integer::compareTo).ifPresent(System.out::println);
Note that deque.stream().max(Integer::compareTo) returns Optional<Integer>.
Alternatively,
deque.stream().flatMapToInt(x -> IntStream.of(x)).max().ifPresent(System.out::println);
Stream#flatMapToInt returns an IntStream consisting of the results of replacing each element of this stream with the contents of a mapped stream produced by applying the provided mapping function to each element.
You can just use the code you have written and extend it by .get().
The following code
public static void main(String[] args) {
// create a Deque that holds Integers
Deque<Integer> myDQ = new ArrayDeque<Integer>();
// add some of them
myDQ.add(12);
myDQ.add(13);
myDQ.add(14);
myDQ.add(15);
myDQ.add(16);
myDQ.add(20);
myDQ.add(17);
myDQ.add(18);
myDQ.add(19);
// and print
System.out.println(
myDQ.stream()
.max(Integer::compareTo) // the largest one
.get() // not as Optional but only as Integer
);
}
just prints 20.
The max-Method returns an java.util.Optional. If you just want to return a int-Value you can use the orElse-Method from java.util.Optional to return the given value or, if not present, another default.
System.out.println(deque.stream().max(Integer::compareTo).orElse(0));
You can unbox the Integer wrappers in your queue and use IntStream.max(), which returns an OptionalInt:
deque.stream().mapToInt(Integer::intValue)
.max()
.ifPresent(System.out::println);
This will do nothing if max() returns OptionalInt.empty(), which happens when the deque is empty. If you want to check for emptiness, you can do, for example:
deque.stream().mapToInt(Integer::intValue)
.max()
.ifPresentOrElse(System.out::println,
() -> throw new RuntimeException("Attempt to get max of empty collection"));

How save a native variable with streams

Hi im trying to understand java 8 api streams, i dont know if this can be done, but im trying to do a filter to a list a save in a new variable like this and the compiler say to me that he cant convert from Stream to String and it has sense but how can i do the operation?
List<String> lista = new ArrayList<String>();
lista.add("1");
lista.add("2");
String num = lista.stream().filter(x -> x.equals("1"));
And if i want to convert that string to int i try to do this but also the compiler say to me, wrong types
List<String> lista = new ArrayList<String>();
lista.add("1");
lista.add("2");
int num = lista.stream().filter(x -> x.equals("1")).map(x -> Integer.parseInt(x));
Streams don't start processing until a terminal method is given to start it. So try this:
List<String> lista = new ArrayList<String>();
lista.add("1");
lista.add("2");
int num = lista.stream().filter(x -> x.equals("1"))
.mapToInt(Integer::parseInt) // use method reference here.
.findFirst().orElse(0);
findFirst returns an OptionalInt so you need to have a default value to return if nothing is found. orElse does that. Also use mapToInt since Integer::parseInt returns an int. Avoids unnecessary boxing. You can apply a similar technique to just get the String when you don't need to parse an int.
You are missing just one call to findFirst:
String num = lista.stream().filter(x -> x.equals("1")).findFirst().get();
And also for the Map:
int num = lista.stream().filter(x -> x.equals("1")).map(x -> Integer.parseInt(x)).findFirst().get();
Be careful when using get method of Optional, if the filter returns nothing it will throw NoSuchElementException.

convert java for loop into stream with filter

I have transformed a regular for loop code into java 8 streams. I tried a few, but i am still learning this and running out of ideas, please suggest ideas. can this be further simplified ? Other than using forEach, I am not able to change much.
Also, why do I have to typecast the eid to String in getERecordFromId((String)eid)
Stream <String>eIdsStream = getEidStream();
final HashSet<String> declinedRecords = new HashSet<>();
eIdsStream.forEach (eid -> {
ERecord eRecord = getERecordFromId((String)eid);
if(eRecord.getEHash() != null && Status.DECLINED == eRecord.getStatus()) {
declineRecords.add(eRecord.getEHash());
}
}
The casting is required since you use a raw Stream variable. Assuming getEidStream() returns a Stream<String>, you should have assigned it to a Stream<String> variable, or not assigned it to a variable at all.
Using forEach defeats the purpose of using Streams in the first place.
You should use filter and map to transform the Stream to hold the required elements, and then collect to a Set.
Set<String> declinedRecords =
getEidStream().map(eid -> getERecordFromId(eid))
.filter(eRecord -> eRecord.getEHash() != null && Status.DECLINED == eRecord.getStatus())
.map(ERecord::getEHash)
.collect(Collectors.toSet());

How to find max length in list of string using streams in Java?

I have a class like below:
public class A
{
String name;
String getName(){return name;}
}
And I also have a list like below:
List<A> list_a = new ArrayList<>();
//add n objects into list_a
Right now I would like to find the max length of object which is in list_a using streams in Java. I have created code like below:
final int max_len = list_a.stream().max(Comparator.comparingInt(A::getName::length));
But it does not work, I mean it is something bad with syntax. Could you help me with this? Thank you.
What you are using isn't lambda. Lambda looks like (arguments) -> action. What you have in A::getName is method reference, but additional ::length is not part of its syntax.
Instead of A::getName::length you can use lambda like a -> a.getName().length().
But your code has yet another problem. Code
list_a.stream()
.max(Comparator.comparingInt(A::getName::length));
is handling streams of A and max method called on Stream<A> will result in Optional<A> not int. It is Optional because there is a chance that list_a can be empty which means that there will be no valid result.
If you want to get OptionalInt you would need to map Stream<A> to Stream<String> and then map it to Stream of ints first. Then you can call its max() method and get:
OptionalInt maxOpt = list_a.stream()
.map(A::getName)
.mapToInt(String::length)
.max();
When you already have OptionalInt you can use it to check if value there isPresent() and get it via getAsInt(). You can also use orElse(defaultValueIfEmpty) like
int max = maxOpt.orElse(-1); //will return value held by maxOpt, or -1 if there is no value
You can use an IntStream as you're just looking for the max length:
OptionalInt oi = list_a.stream()
.map(A::getName)
.mapToInt(String::length)
.max()
final int max_len = oi.orElse(0); //defaulting to 0
If you need to use a custom comparator, you will need a lambda expression:
final int max_len = list_a.stream()
.max(Comparator.comparingInt(a ->
a.getName().length())) //need a lambda
.map(A::getName)
.map(String::length)
.orElse(0); //defaulting to 0
Alternative solution using Collections.max:
A a = Collections.max(list_a, Comparator.comparing(obj -> obj.getName().length()));
int maxLen = a.getName().length();
Keep in mind that Collections.max throws NoSuchElementException if the collection is empty. If you don't want it, use the approach with OptionalInt like in #Pshemo's answer.

Streams : how map in Streams work

To understand the map function in Streams better I was trying something like this:
String inputString="1+3+5";
Stream.of(inputString.split("\\+")).map(
eachStringLiteral -> {
output += mapOfStringAndNumber.get(eachStringLiteral) + literal;
}
);
Where inputString is:
String inputString = "1+3+5";
however, the compiler complains and I don't know why:
The method map(Function) in the type
Stream is not applicable for the arguments ((
eachStringLiteral) -> {})
I also need some help in order to understand the syntax in
Function<? super String,? extends R>.
Update
This is whole code illustrating what I am trying to achieve:
HashMap<String,Double> mapOfStringAndNumber=new HashMap<String,Double>();
mapOfStringAndNumber.put("1",270.5);
mapOfStringAndNumber.put("2",377.5);
mapOfStringAndNumber.put("3",377.5);
String inputString="1+3+5";
String literal="+";
String output;
java.util.stream.Stream.of(inputString.split("+")).map(eachStringLiteral->
output+=mapOfStringAndNumber.get(eachStringLiteral)+literal
Assuming that you provide a Double value to each input String in your map mapOfStringAndNumber, you could use Pattern#splitAsStream(CharSequence input) to split your input String directly as a Stream and use Collectors.joining(CharSequence delimiter) to build your output with + as delimiter, so your final code could be:
Map<String,Double> mapOfStringAndNumber = new HashMap<>();
mapOfStringAndNumber.put("1",270.5);
mapOfStringAndNumber.put("3",377.5);
mapOfStringAndNumber.put("5",377.5);
String inputString = "1+3+5";
String output = Pattern.compile("\\+")
.splitAsStream(inputString)
.map(mapOfStringAndNumber::get)
.map(d -> Double.toString(d))
.collect(Collectors.joining("+"));
System.out.println(output);
Output:
270.5+377.5+377.5
I have a couple of suggestions for your code:
If you use a {}-block and you want to create a Function (not a Runnable, Consumer or similar) you have to return something.
Unless you have a very special case, use Integer.parseInt(String) instead of a Map of Strings to Integers
You must not modify output from within a Stream. That could lead to concurrancy issues.
Do you really need literal?
My example code for your usecase would look like this:
String inputString="1+3+5";
return
// split input and create stream
Stream.of(inputString.split("\\+"))
// convert each item to Integer
.mapToInt(Integer::parseInt)
// sum up everything
.sum();
map, by definition, takes an input stream and applies a Function to each element of this stream, creating a new stream out of those modified elements.
So the count of items in input stream has to be the same as the count of items in output stream.
The error you are mentioning occurs, because your mapping function does not return anything: output+=mapOfStringAndNumber.get(eachStringLiteral)+literal only modifies the output variable and returns nothing.
From what I'm seeing here, you want to perform map and reduce operations:
map operation changes each element of input stream into its value from mapOfStringAndNumber and adding literal to it
reduce operation, in your case, is summing up all elements of your stream
To achieve this using Java 8 features, you need to first map the elements of your stream and then sum them up. You can do it like this:
String sum = Stream.of(inputString.split("\\+"))
.map(stringLiteral->
mapOfStringAndNumber.get(stringLiteral).toString())
//join all the strings in the stream with a provided delimiter
.collect(Collectors.joining(literal));
output+=mapOfStringAndNumber.get(eachStringLiteral)+literal; is an expression. Lambda expressions that have a body consisting of a single expression should not have them placed within curly braces:
Stream.of(inputString.split("+")).map(eachStringLiteral ->
output+=mapOfStringAndNumber.get(eachStringLiteral)+literal;
);
See the section of the Java Language Specification concerning lambda bodies:
A lambda body is either a single expression or a block (ยง14.2)...
LambdaBody:
Expression
Block
Alternatively you can just add a return clause before the output variable to make it a statement.

Categories

Resources