Print modified list values using Java 8 Stream - java

Having a list like the following one
List<Integer> values = new ArrayList<Integer>();
values.add(1);
values.add(0);
values.add(1);
values.add(1);
values.add(0);
I want to print the elements > 0 adding them a value, for example 10, by using Java 8 Stream. For example:
values.stream()
.filter(val -> val > 0)
// HERE add 10
.forEach(System.out::println);
Is it possible to do that? If yes, how?

Use the map operation
values.stream()
.filter(val -> val>0)
.map(x -> x+10)
.forEach(System.out::println);
If you need to keep the values, do
List<Integer> newValues = values.stream()
.filter(val -> val>0)
.map(x -> x+10)
.collect(Collectors.toList());

If you want to print and save new data in one go you can use peek() like below -
List<Integer> newValues = myList.stream().filter(val -> val>0).map(x -> x+10)
.peek(System.out::println).collect(Collectors.toList());

values = StreamSupport.stream(values)
.filter(val -> val > 0)
.forEach(val -> val += 10)
.collect(Collectors.toList());
then you can use:
values.stream()
.forEach(System.out::println);

Related

Why is map stream sorted after filtering not working?

I have Map<String,Integer> map. I want to filter and sort the map by key and then get 5 percent of their number , I have such a function:
public List<String> getValuableSubStr(){
List<String> result = new ArrayList<>();
long size = map.entrySet().stream().filter(e -> e.getKey().length() ==3).count();
map.entrySet().stream()
.filter(e -> e.getKey().length() ==3)
.sorted(Map.Entry.<String,Integer>comparingByKey().reversed())
.limit((size*5)/100)
.peek(e -> result.add(e.getKey()));
return result;
}
But after calling the function , I get an empty list , although the map is not empty and forEach are printed normally .What did I do wrong?
peek is not a terminal operation, so it doesn't cause the stream to be evaluated.
Use forEach instead of peek.
Or, better, collect directly into a list.
return map.entrySet().stream()
.filter(e -> e.getKey().length() ==3)
.sorted(Map.Entry.<String,Integer>comparingByKey().reversed())
.limit((size*5)/100)
.map(Map.Entry::getKey)
.collect(Collectors.toList());
Actually, since you're dealing only with keys:
return map.keySet().stream()
.filter(e -> e.length() ==3)
.sorted(Comparator.reverseOrder())
.limit((size*5)/100)
.collect(Collectors.toList());
peek is more useful for debugging. See here:
it's an intermediate operation and we didn't apply a terminal operation
from https://www.baeldung.com/java-streams-peek-api
I would do
public List<String> getValuableSubStr(){
List<String> result = new ArrayList<>();
long size = map.entrySet().stream().filter(e -> e.getKey().length() ==3).count();
return map.entrySet().stream()
.filter(e -> e.getKey().length() ==3)
.sorted(Map.Entry.<String,Integer>comparingByKey().reversed())
.limit((size*5)/100)
.map(a -> a.getKey())
.collect(Collectors.toList());
}

Filter in Java when meeting conditions

There is a condition that I need values in the following Set
Set<String> name = studentResponse
.stream()
.map(StudentResponse::getDetails)
.flatMap(List::stream)
.map(StudentDetail::getName())
.filter(s -> s.contains("A"))
.collect(Collectors.toSet());
I want to filter student names containing "A" if List<StudentDetail> details in StudentResponse contains more than 5 elements. If not, I want to take all names in StudentDetail. Is there any way to handle this condition?
You can use
Set<String> name = studentResponse
.stream()
.map(StudentResponse::getDetails)
.flatMap(l -> l.stream()
.map(StudentDetail::getName)
.filter(s -> l.size() <= 5 || s.contains("A")))
.collect(Collectors.toSet());
but it has the disadvantage of re-checking a list condition for every element, despite it shouldn't change during the entire traversal. A better solution is not to perform a filter operation when it is not necessary, like
Set<String> name = studentResponse
.stream()
.map(StudentResponse::getDetails)
.flatMap(l -> l.size() <= 5?
l.stream().map(StudentDetail::getName):
l.stream().map(StudentDetail::getName).filter(s -> s.contains("A")))
.collect(Collectors.toSet());
or, to avoid the code duplication:
Set<String> name = studentResponse
.stream()
.map(StudentResponse::getDetails)
.flatMap(l -> {
Stream<String> names = l.stream().map(StudentDetail::getName);
return l.size() <= 5? names: names.filter(s -> s.contains("A"));
})
.collect(Collectors.toSet());
Other way is using Supplier<T>
Supplier<Stream<List<StudentDetail>>> stuSup = ()-> studentResponse
.stream()
.map(StudentResponse::getDetails);
then perform a filter on it.
Stream<String> gtFive = stuSup.get()
.filter(d->d.size()>5)
.flatMap(List::stream)
.map(StudentDetail::getName())
.filter(s -> s.contains("A"));
and for less than five:
Stream<String> lteFive = stuSup.get()
.filter(d->d.size()<=5)
.flatMap(List::stream)
.map(StudentDetail::getName());
and finally, combine both of them.
Stream.concat(gtFive,lteFive).collect(toSet());
You can try out processing the two halves based on the conditions using partitioningBy.
Map<Boolean, List<StudentResponse>> greaterThanFive = studentResponse.stream()
.collect(Collectors.partitioningBy(sr -> sr.getDetails().size() > 5));
Set<String> names = Stream.concat(
greaterThanFive.get(Boolean.FALSE).stream()
.flatMap(sr -> sr.getDetails().stream())
.map(StudentDetail::getName),
greaterThanFive.get(Boolean.TRUE).stream()
.flatMap(sr -> sr.getDetails().stream())
.map(StudentDetail::getName)
.filter(name -> name.contains("A"))) // for details size more than 5
.collect(Collectors.toSet());
But there is no reason to choose it over a solution that can perform the partitioning on-the-fly.
You can use Map.Entry as a bag to collect all informations that are needed in the last filtering.
Set<String> name = studentResponse
.stream()
.flatMap(sr -> sr.getDetails().stream().map(
d -> Map.entry(sr.getDetails().size(), d.getName())))
.filter(e -> (e.getKey() <= 5) || (e.getKey() > 5 && e.getValue().contains("A")))
.map(Map.Entry::getValue)
.collect(Collectors.toSet());

How to process object failed to satisfy the filter in java 8 stream

I am trying to process object which is failed to satisfy the filter condition in the stream.
List<Integer> list = Arrays.asList(1,23,43,12,4,5);
list.stream().filter( i -> i > 10).collect(Collections.toList);
This will return a list of Object greater than 10. but I also want to process the objects which are unable to satisfy the condition (>10).
Thank You.
Map<Boolean, List<Integer>> map = list.stream()
.collect(Collectors.partitioningBy(i > 10));
map.get(false/true).... do whatever you want with those that failed or not
I would just run two sweeps with stream() to get two different lists:
List<Integer> list = Arrays.asList(1,23,43,12,4,5);
List<Integer> largerThanTen = list.stream().filter( i -> i > 10)
.collect(Collectors.toList());
List<Integer> smallerOrEqualToTen = list.stream().filter( i -> i <= 10)
.collect(Collectors.toList());
I think this is more readable than trying to do it in a one-liner, resulting in a less-idiomatic data structure.
List<Integer> list = Arrays.asList(1,23,43,12,4,5);
list.stream().filter( i -> i > 10).collect(Collections.toList);
change to
Map < Boolean, List < Integer > > map = Stream.of( 1, 23, 43, 12, 4, 5 ).collect( Collectors.groupingBy( e -> e > 10 ) );
then you can use:
map.get( false )// is list of has not condition
map.get(true) // is list of has condition

Java stream create object and compare

I'm pretty new to Java streams. I've to split a string returned by filter in stream, create a new object with the strings in the split and compare each object with a predefined object. Stream looks like this (I know this is incorrect, just a representation of what I am trying to do):
xmlstream.stream()
.filter(xml->xml.getName()) //returns a string
.map(returnedString -> split("__"))
.map(eachStringInList -> new TestObj(returnedStr[0], returnedStr[1]))
.map(eachTestObj -> eachTestObj.compareTo(givenObj))
.max(Comparing.compare(returnedObj :: aProperty))
How do I achieve this? Basically map each string in list to create an object, compare that to a fix object and return max based on one of the properties.
Thanks.
You could use reduce like so:
TestObj predefined = ...
TestObj max =
xmlstream.stream()
.map(xml -> xml.getName()) //returns a string
.map(s -> s.split("__"))
.map(a -> new TestObj(a[0], a[1]))
.reduce(predifined, (e, a) ->
e.aProperty().compareTo(a.aProperty()) >= 0 ? e : a);
A more efficient version of the above would be:
TestObj predefined = ...
TestObj max =
xmlstream.stream()
.map(xml -> xml.getName()) //returns a string
.map(s -> s.split("__"))
.map(a -> new TestObj(a[0], a[1]))
.filter(e -> e.aProperty().compareTo(predefined.aProperty()) > 0)
.findFirst()
.orElse(predefined);
Update:
if you want to retrieve the max object by a given property from all the TestObj objects less than the predefined TestObj, then you can proceed as follows:
TestObj predefined = ...
Optional<TestObj> max =
xmlstream.stream()
.map(xml -> xml.getName())
.map(s -> s.split("_"))
.map(a -> new TestObj(a[0], a[1]))
.filter(e -> e.aProperty().compareTo(predefined.aProperty()) < 0)
.max(Comparator.comparing(TestObj::aProperty));
max returns an Optional<T>; if you're unfamiliar with it then consult the documentation here to familiarise you're with the different ways to unwrap an Optional<T> object.

Convert Map of maps into Lists using Java 8 Streams

I have a map:
Map<String, Map<Integer, List<Integer>>>
e.g. Map<Name, Map<Id, List<ReferenceId>>>
Outcome:
List<Id>
List<ReferenceId>
I wanna convert this map into two list of Integers. One list contains inner-map keys, and other contains inner-map value (i.e. List<Integer>)
Can anyone tell me how to do this in Java 8 using streams?
I tried this way but got Cast Exception, can not convert String to Integer.
map.values().stream()
.map(m -> m.entrySet()
.stream()
.map(e -> e.getKey())
.collect(Collectors.toList()))
.flatMap(l -> l.stream())
.collect(Collectors.toList());
Map<String, Map<Integer, List<Integer>>> map = ...
List<Integer> keys = map.values() // Collection<Map<Integer, List<Integer>>>
.stream() // Stream<Map<Integer, List<Integer>>>
.map(Map::keySet) // Stream<Set<Integer>>
.flatMap(Set::stream) // Stream<Integer>
.collect(Collectors.toList()); // List<Integer>
List<Integer> values = map.values() // Collection<Map<Integer, List<Integer>>>
.stream() // Stream<Map<Integer, List<Integer>>>
.map(Map::values) // Stream<Collection<List<Integer>>>
.flatMap(Collection::stream) // Stream<List<Integer>>
.flatMap(List::stream) // Stream<Integer>
.collect(Collectors.toList()); // List<Integer>
There is no way, how your code
List<Integer> list = map.values().stream()
.map(m -> m.entrySet().stream()
.map(e -> e.getKey())
.collect(Collectors.toList()))
.flatMap(l -> l.stream())
.collect(Collectors.toList());
can produce a ClassCastException, unless you managed to insert objects of wrong type into the source map via unchecked operation(s) before the Stream operation. Such a situation is called heap pollution and you should compile your entire code with all warnings enabled (javac: use option -Xlint:unchecked) and solve them.
But note that your code is unnecessarily complicated. The chain, .entrySet().stream().map(e -> e.getKey()) is streaming over the entries and mapping to the keys, so you can stream over the keys in the first place, i.e. .keySet().stream(). Then, you are collecting the stream into a List, just to invoke .stream() in the subequent flatMap step, so you can simply use the stream you already have instead:
List<Integer> list = map.values().stream()
.flatMap(m -> m.keySet().stream())
.collect(Collectors.toList());
Alternatively, you can let the collector do all the work:
List<Integer> list = map.values().stream()
.collect(ArrayList::new, (l,m) -> l.addAll(m.keySet()), List::addAll);
Getting the values instead of the keys works similar, but requires another flatMap step to get the List elements:
List<Integer> list = map.values().stream()
.flatMap(m -> m.values().stream().flatMap(List::stream))
.collect(Collectors.toList());
which is equivalent to
List<Integer> list = map.values().stream()
.flatMap(m -> m.values().stream())
.flatMap(List::stream)
.collect(Collectors.toList());
Again, there’s the alternative of letting the collector do all the work:
List<Integer> list = map.values().stream()
.collect(ArrayList::new, (l,m)->m.values().forEach(l::addAll), List::addAll);
or
List<Integer> list = map.values().stream()
.collect(ArrayList::new, (l,m)->m.forEach((k,v)->l.addAll(v)), List::addAll);
If your value is like Map<String,Object>. And your Object is like Map<String,Object>:
Set<String> mapKeys = myMap.entryset() //Set<Entry<String,Object>>
.stream() //Stream<Entry<String,Object>>
.map(m->(Map<String,Object>) m.getValue()) //Stream<Map<String,Object>>
.map(Map::keySet) //Stream<Set<String>>
.flatMap(l->l.stream()) //Stream<String>
.collect(Collectors.toSet())
it works
Adding for one more working scenario
Map<Integer, List<Integer>> existingPacakagesMap = // having value {123=[111, 222, 333], 987=[444, 555, 666]}
Retrieving logic
List<Integer> ListOfAllPacakages= existingPacakagesMap.values().stream().flatMap(List::stream).collect(Collectors.toList());
Result will be
ListOfAllPacakages= [111, 222, 333, 444, 555, 666]

Categories

Resources