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.
Related
I am wondering, whether it makes sense to create a reusable IntStream for the following problem and how to do so.
Basically, I need to loop over an int[] of numbers to be detected and and count the frequency of these numbers in another int[].
If I had an IntStream-Supplier, it was reusable in some loop. So, I am looking for something like
int[] intsToBeDectected = new int[]{1,2,3}
int[] numbers = new int[]{1,1,1,2,3,3,3,3,3}
Supplier<IntStream> supplier = IntStream.of(numbers); // ERROR
for (int i : intsToBeDetected){
int freq = (int) supplier.get().filter(n -> n=i).count();
}
From object streams in java, I know something like this:
Supplier<Stream<Object>> supplier = () -> Arrays.stream(objects)
This stream of objects is reusable like:
supplier.get().filter()...
However, I somehow fail to transfer this idea to IntStreams - It's not compiling in the line commented with ERROR.
Does it make sense to transfer this concept? If yes, how to do so?
Replace
Supplier<IntStream> supplier = IntStream.of(numbers); // ERROR
with
Supplier<IntStream> supplier = () -> IntStream.of(numbers);
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());
I have an custom class InfoAQ which has a method called public String getSeqInf(). Now I have an ArrayList<InfoAQ> infList and
I need an ArrayList<String>strList = new ArrayList<String>with the content from getSeqInf()for each element.
This is the way Im doing it right now ...
for(InfoAQ currentInf : infList)
strList.add(currentInf.getSeqInf());
Is there an alternative way to make it ? Maybe a faster one or one liner ?
Yes, there is:
strList = infList.stream().map(e -> g.getSeqInf()).collect(Collectors.toList());
The map step can be also written in another way:
strList = infList.stream().map(InfoAQ::getSeqInf).collect(Collectors.toList());
which is know as method reference passing. Those two solutions are equivalent.
Also maybe this one:
List<String> strList = new ArrayList<String>();
infList.forEach(e -> strList.add(e.getSeqInf()));
And there is another one (-liner, if you format it in a single line):
infList.forEach(currentInf -> {strList.add(currentInf.getSeqInf());});
while I would prefer a formatting in more lines:
infList.forEach(currentInf -> {
strList.add(currentInf.getSeqInf());
});
Using streams
infList.stream()
.map(InfoAQ::getSeqInf)
.collect(Collectors.toCollection(ArrayList::new))
Using Collectors.toCollection here to create an ArrayList that will hold the results as you do in your case. (Important if you do care about the result list type as Collectors.toList() does not guarantee this)
May not be the fastest as using stream has some overhead. You need to measure/benchmark to find out its performance
This code will iterate all the data in the list, as getSeqInf returns a String, the collect method will store all returns of the getSeqInf method in a list.
`List listString = infList.stream().map(InfoAQ::getSeqInf).collect(Collectors.toList());`
or
`
ArrayList<String> listString = new ArrayList<>();
for(int i = 0; i < infoAq.size(); i++) {
listString.add(infoAq.get(i).getSeqInf());
}`
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
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.