convert nested for loop logic using stream - java

I have a set of code which I want to perform using stream I have done using for loop but unable to do it using stream or other better approach please advice
for (Student student : students) {
List<Laptop> filteredLaptop = new ArrayList<>();
for (Laptop laptop: student.getLaptopList()) {
if(laptop.getColour().endsWith("RED")){
filteredLaptop.add(laptop);
}
}
student.setLaptopList(filteredLaptop);
}
How can i reduce the complexity of code or how can perform same operation using Stream

Something like this (I think it's better to rename laptopsList)
students.forEach(student ->
student.setLaptops(
student.getLaptops().stream()
.filter(laptop -> laptop.getColour().endsWith("RED"))
.collect(toList())
)
);

Related

Java Stream filter/peek two different conditions in the same section

I'm trying to filter two conditions in a stream that is inside another stream of data, what I need to do is to check if the object is there by using the "name" parameter and then reviewing the Boolean property of "isGoldplated" if is true, I tried using this code but didn't work as it wasn't filtering by the isGoldplated parameter:
List<CompressorModel> filteredCompressors = pack.getSet().getCompressors().stream()
.peek(p -> goldData.stream().map(GoldPlateData::getCompressorSerialNo).anyMatch(name -> name.equals(p.getGcsn())))
.peek(p -> goldData.stream().map(GoldPlateData::getIsGoldplated))
.collect(Collectors.toList());
so I finished using two loops instead:
List<CompressorModel> filteredCompressors = new ArrayList<>();
for (CompressorModel cmp : pack.getSet().getCompressors()) {
for(GoldPlateData gold: goldData) {
if( StringUtils.equalsIgnoreCase(cmp.getGcsn(), gold.getCompressorSerialNo()) && Boolean.TRUE.equals(gold.getIsGoldplated())) {
filteredCompressors.add(cmp);
}
}
}
so my request is, how could I convert these two loops into a working stream?
thanks in advance
You could use filter() on the pack.getSet().getCompressors() stream and then look for a match in goldData, like this:
List<CompressorModel> filteredCompressors = pack.getSet()
.getCompressors().stream()
.filter(cmp -> goldData.stream().anyMatch(gd -> cmp.getGcsn().equalsIgnoreCase(gd.getCompressorSerialNo()) && gd.getIsGoldplated()))
.collect(Collectors.toList());
Peek is basically there only to be used for debugging purposes. Peek can be not executed at all at times because it's a terminal operation. You can see here to get an idea.
So you may modify your implementation to use filter instead.
List<CompressorModel> filteredCompressors = pack.getSet().getCompressors().stream()
.filter(p -> goldData.stream().map(GoldPlateData::getCompressorSerialNo).anyMatch(name -> name.equals(p.getGcsn())))
.filter(p -> goldData.stream().map(GoldPlateData::getIsGoldplated))
.collect(Collectors.toList());

Java stream: iterate two collections, chaining response

I have following block, processRule() removes entries from diff list.
public List<Difference> process(List<Rule> rules, List<Difference> differences) {
for (Rule rule : rules) {
differences = processRule(rule, differences);
}
return differences;
}
how can this be done with stream api? i can't just use flatMap because i need each new call to processRule() to have reduced differences as an argument.
May be something like this using stream reduce.
Note: not tested, posting from my mobile
return rules
.stream()
.reduce(differences, (rule1, rule2) ->
processRule(rule2,
processRule(rule1, differences))
} );

Java loop with a replaceFirst Method

this is my first post on Stack Overflow, so please, be forgiving! :)
I have a list with 403 Polish registration plates symbols and counties. It looks like this:
BIA powiat białostocki
BBI powiat bielski
BGR powiat grajewski
CT Toruń
etc.
I made a code which let me to turn the first space into "=".
import java.io.*;
public class Test {
public static void main(String args[]) {
String Str = new String("BAU powiat augustowski");
System.out.println(Str.replaceFirst(" ", "="));
}
}
How can I make a loop (for? do while?) to change all 403 records? I would appreciate any help. Thank you in advance!
If your list is a List<String>, you can do this :
for (for int i = 0, i < yourList.size(), i++) {
yourList.set(i, yourList.get(i).replaceFirst(" ", "="));
}
Other ways to loop are available here : https://crunchify.com/how-to-iterate-through-java-list-4-way-to-iterate-through-loop/
Best
You COULD also use Stream API. Eg if you want to filter all invalid strings
List<String> registrations = new ArrayList<>(5);
registrations.add("BIA powiat białostocki");
registrations.add("BBI powiat bielski");
registrations.add("BGR powiat grajewski");
registrations.add("BGGHND");
registrations.add("CT Toruń etc.");
registrations = registrations.stream()
.filter(registration -> registration.split(" ").length>1)
.map(registration -> registration.replaceFirst(" ","="))
.collect(Collectors.toList());
Output:
BIA=powiat białostocki
BBI=powiat bielski
BGR=powiat grajewski
CT=Toruń etc.
As you mentioned how can make a loop then I would suggest to learn java loop syntax at first. Following tutorial can be helpful
https://books.trinket.io/thinkjava2/chapter6.html
https://www.codingame.com/playgrounds/6162/6-ways-to-iterate-or-loop-a-map-in-java
Regarding the solution, you can loop your lists using for/while loop or using Stream API. Here is your solution using stream API:
List<String> lists = new ArrayList<>();
lists.add("BAU powiat augustowski");
lists.add("BBI powiat bielski");
lists = lists.stream()
.map(s -> s.replaceFirst("\\s", "="))
.collect(toList());
Either if you are using an ArrayList or a HashSet you can use two ways:
Fyi: assuming your lists name is registrationList and it holds Objects names Registration
Either a for loop:
for(Registration registration : registrationList){
registration.replaceFirst(" ", "=");
}
Or you can use a Stream:
registrationList.stream.forEach(registration-> registration.replaceFirst(" ", "="));
If you have all lines in txt file and you want to modify that by replacing first space to = you could use stream API like:
List<String> collect = Files.lines(Paths.get(PATH_TO_FILE)).stream()
.map(s -> s.replaceFirst(" ", "="))
.collect(toList());
Files.write(PATH_TO_FILE, collect, StandardOpenOption.CREATE);
See more StandardOpenOption

Replace nested for loops with parallel stream - Java

I'm working on improving the speed of a program where performance is critical. Currently it fails to process large data sets. There are many nested for loops and so I thought it would be worth trying parallel streams. I have access to a high performance cluster so potentially have many cores available.
I have the method below:
public MinSpecSetFamily getMinDomSpecSets() {
MinSpecSetFamily result = new MinSpecSetFamily();
ResourceType minRT = this.getFirstEssentialResourceType();
if (minRT == null || minRT.noSpecies()) {
System.out.println("Problem in getMinDomSpecSets()");
}
for (Species spec : minRT.specList) {
SpecTree minTree = this.getMinimalConstSpecTreeRootedAt(spec);
ArrayList<SpecTreeNode> leafList = minTree.getLeaves();
for (SpecTreeNode leaf : leafList) {
ArrayList<Species> sp = leaf.getAncestors();
SpecSet tmpSet = new SpecSet(sp);
result.addSpecSet(tmpSet);
}
}
return result;
}
I understand that I can turn a nested for loop into a parallel stream with something like:
minRT.specList.parallelStream().flatMap(leaf -> leaflist.parallelStream())
However, I cannot find examples showing how to deal with the actions inside each for loop and I'm not at all confident about how this is supposed to work. I'd really appreciate some assistance and explanation of how to convert this method so that I can translate the solution to other methods in the program too.
Thanks.
Here's one way of doing it (hopefully I have no typos):
MinSpecSetFamily result =
minRT.specList
.parallelStream()
.flatMap(spec -> getMinimalConstSpecTreeRootedAt(spec).getLeaves().stream())
.map(leaf -> new SpecSet(leaf.getAncestors()))
.reduce(new MinSpecSetFamily (),
(fam,set)-> {
fam.addSpecSet(set);
return fam;
},
(f1, f2) -> new MinSpecSetFamily(f1, f2));
EDIT: Following Holger's comment, you should use collect instead of reduce:
MinSpecSetFamily result =
minRT.specList
.parallelStream()
.flatMap(spec -> getMinimalConstSpecTreeRootedAt(spec).getLeaves().stream())
.map(leaf -> new SpecSet(leaf.getAncestors()))
.collect(MinSpecSetFamily::new,MinSpecSetFamily::addSpecSet,MinSpecSetFamily::add);

Multiple conditions to filter a result set using Java 8

I am looking for some help in converting some code I have to use the really nifty Java 8 Stream library. Essentially I have a bunch of student objects and I would like to get back a list of filtered objects as seen below:
List<Integer> classRoomList;
Set<ScienceStudent> filteredStudents = new HashSet<>();
//Return only 5 students in the end
int limit = 5;
for (MathStudent s : mathStudents)
{
// Get the scienceStudent with the same id as the math student
ScienceStudent ss = scienceStudents.get(s.getId());
if (classRoomList.contains(ss.getClassroomId()))
{
if (!exclusionStudents.contains(ss))
{
if (limit > 0)
{
filteredStudents.add(ss);
limit--;
}
}
}
}
Of course the above is a super contrived example I made up for the sake of learning more Java 8. Assume all students are extended from a Student object with studentId and classRoomId. An additional requirement I would require is the have the result be an Immutable set.
A quite literal translation (and the required classes to play around)
interface ScienceStudent {
String getClassroomId();
}
interface MathStudent {
String getId();
}
Set<ScienceStudent> filter(
Collection<MathStudent> mathStudents,
Map<String, ScienceStudent> scienceStudents,
Set<ScienceStudent> exclusionStudents,
List<String> classRoomList) {
return mathStudents.stream()
.map(s -> scienceStudents.get(s.getId()))
.filter(ss -> classRoomList.contains(ss.getClassroomId()))
.filter(ss -> !exclusionStudents.contains(ss))
.limit(5)
.collect(Collectors.toSet());
}
Multiple conditions to filter really just translate into multiple .filter calls or a combined big filter like ss -> classRoomList.contains(ss.getClassroomId()) && !exclusion...
Regarding immutable set: You best wrap that around the result manually because collect expects a mutable collection that can be filled from the stream and returned once finished. I don't see an easy way to do that directly with streams.
The null paranoid version
return mathStudents.stream().filter(Objects::nonNull) // math students could be null
.map(MathStudent::getId).filter(Objects::nonNull) // their id could be null
.map(scienceStudents::get).filter(Objects::nonNull) // and the mapped science student
.filter(ss -> classRoomList.contains(ss.getClassroomId()))
.filter(ss -> !exclusionStudents.contains(ss))
.limit(5)
.collect(Collectors.toSet());

Categories

Resources