Convert nested loops according to Java 8 nested loops - java-8

Found difficulty to change this code according to java8 lambda expression.
The code removes student object which have same name and add that object into a single entry.
public List<Student> removeDuplicates(List<Student> studentList)
{
List<Student> newStudentList=new ArrayList<Student>();
List<String> studentNames= new ArrayList<String>();
studentList.stream().forEach(e->studentNames.add(e.getName()));
List<String> studentNameList= new ArrayList<String>();
for (Student student: studentList) {
int count=0;
for (String sname: studentNames)
{
if(student.getName().equals(sname)){
count++;
}
else{
count=0;
}
if(count==1)
{ if(!studentNameList.contains(student.getName())){
studentNameList.add(student.getName());
newStudentList.add(student);
}
}
}
}
}

Try this
public Stream<Student> removeDuplicates(List<Student> studentList) {
return studentList
.stream()
.collect(
Collectors.toMap(Student::getName, s -> s,
(fst, snd) -> fst)).values().stream();
}

Here you are using java-8 streams.
public List<Student> removeDuplicates(List<Student> studentList) {
return studentList.stream()
.map(Student::getName)
.distinct()
.map(x -> studentList.stream()
.filter(y -> y.getName().equals(x))
.findFirst()
.get())
.collect(Collectors.toList());
}

I assume you must end up with a List, otherwise you would have chosen to use a Set which by definition is a unique collection. A Set could still be part of the solution, however, doing something like
Set<String> tempUniqueValues = new HashSet<>();
List<String> listWithoutDupes = myList.stream()
.filter(tempUniqueValues::add) // filters out non-unique values
.collect(Collectors.toList());

Related

How to get all keys whose values are null in Java 8 using Map

I was going through How to remove a key from HashMap while iterating over it?, but my requirement is bit different.
class Main {
public static void main(String[] args) {
Map<String, String> hashMap = new HashMap<>();
hashMap.put("RED", "#FF0000");
hashMap.put("BLACK", null);
hashMap.put("BLUE", "#0000FF");
hashMap.put("GREEN", "#008000");
hashMap.put("WHITE", null);
// I wan't result like below - get All keys whose value is null
List<String> collect = hashMap.values()
.stream()
.filter(e -> e == null)
.collect(Collectors.toList());
System.out.println(collect);
// Result - BLACK, WHITE in list
}
}
Try this:
import java.util.*;
import java.util.stream.*;
class Main {
public static void main(String[] args) {
Map<String, String> hashMap = new HashMap<>();
hashMap.put("RED", "#FF0000");
hashMap.put("BLACK", null);
hashMap.put("BLUE", "#0000FF");
hashMap.put("GREEN", "#008000");
hashMap.put("WHITE", null);
// I wan't result like below - get All keys whose value is null
List<String> collect = hashMap.keySet()
.stream()
.filter(e -> Objects.isNull(hashMap.get(e)))
.collect(Collectors.toList());
System.out.println(collect);
// Result - BLACK, WHITE in list
}
}
As pointed out in the comments, you can try this as well:
import java.util.*;
import java.util.stream.*;
class Main {
public static void main(String[] args) {
Map<String, String> hashMap = new HashMap<>();
hashMap.put("RED", "#FF0000");
hashMap.put("BLACK", null);
hashMap.put("BLUE", "#0000FF");
hashMap.put("GREEN", "#008000");
hashMap.put("WHITE", null);
// I wan't result like below - get All keys whose value is null
List<String> collect = hashMap.entrySet()
.stream()
.filter(e -> Objects.isNull(e.getValue()))
.map(e -> e.getKey())
.collect(Collectors.toList());
System.out.println(collect);
// Result - BLACK, WHITE in list
}
}
This is more optimized, as compared to the first solution.

How to write the below code in Java 8 using lambda expressions?

I have the following classes:
package com.test.api;
public class Driver {
private String name;
private int age;
private String gender;
// getters, setters
}
package com.test.api;
import java.util.List;
public class Vehicle {
private String name;
private String make;
private int modelyear;
private List<Driver> drivers;
// getters, setters
}
package com.test.api;
public class Data {
private String driverName;
private String vehicleName;
private String gender;
private boolean isDriverEligible;
private boolean isRecentVehicle;
// getters, setters
}
and I am currently doing this in traditional way without lamdas and streams:
private static List<Data> map(List<Vehicle> vehicleList) {
List<Data> dataList = null;
for (Vehicle vehicle : vehicleList) {
if ("BMW".equalsIgnoreCase(vehicle.getName())) {
dataList = new ArrayList<>();
for (Driver driver : vehicle.getDrivers()) {
if (driver.getAge() > 18 && vehicle.getModelyear() > 2016) {
Data data = new Data();
data.setDriverName(driver.getName());
data.setLuxuryVehicle(true);
data.setVehicleName(vehicle.getName());
data.setDriverEligible(true);
data.setRecentVehicle(true);
dataList.add(data);
}
if (driver.getAge() > 18 && vehicle.getModelyear() < 2016) {
Data data = new Data();
data.setDriverName(driver.getName());
data.setLuxuryVehicle(true);
data.setVehicleName(vehicle.getName());
data.setDriverEligible(true);
data.setRecentVehicle(false);
dataList.add(data);
}
}
List<Response> response = service.update(dataList, vehicle);
}
}
return dataList;
}
So I wanted to iterate through List of Drivers for each Vehicle and based on the conditions, I need to map some data. And then once all the data is populated to the "Data" List, I am updating it in to a service. Everything looks good but I wanted to refactor it using java 8.How can I do it?
Prior notice: If you are running this against a database, it's HIGHLY RECOMMENDED to do that with some SQL or stored procedures. Messing with data in the application is not considered a good practice unless it's absolutely necessary.
The idea of using stream is to describe filtering and mapping in distinct blocks.
Let's start simple, from the inner loop.
private static List<Data> map(List<Vehicle> vehicleList) {
List<Data> dataList = new ArrayList<>();
vehicleList.stream().filter(vehicle -> "BMW".equalsIgnoreCase(vehicle.getName())).forEach(vehicle -> {
dataList.addAll(
return vehicle.getDrivers().stream()
.filter(driver -> driver.getAge() > 18)
.map(driver -> {
Data data = new Data();
data.setDriverName(driver.getName());
data.setLuxuryVehicle(true);
data.setVehicleName(vehicle.getName());
data.setDriverEligible(true);
if (vehicle.getModelyear() > 2016) {
data.setRecentVehicle(true);
return data;
}
if (vehicle.getModelyear() < 2016) {
data.setRecentVehicle(false);
return data;
}
return null;
})
.filter(it -> it != null)
.collect(Collectors.toList())
);
List<Response> response = service.update(dataList, vehicle);
});
return dataList;
}
We have replaced for loops with a stream. Since we are going to add it into a list, we can map the elements into target elements, and then collect them and call addAll(.) to append them to the list.
Then we can make it to the outer loop:
private static List<Data> map(List<Vehicle> vehicleList) {
List<Data> dataList = new ArrayList<>();
vehicleList.stream().filter(vehicle -> "BMW".equalsIgnoreCase(vehicle.getName())).forEach(vehicle -> {
dataList.addAll(
return vehicle.getDrivers().stream()
.filter(driver -> driver.getAge() > 18)
.map(driver -> {
Data data = new Data();
data.setDriverName(driver.getName());
data.setLuxuryVehicle(true);
data.setVehicleName(vehicle.getName());
data.setDriverEligible(true);
if (vehicle.getModelyear() > 2016) {
data.setRecentVehicle(true);
return data;
}
if (vehicle.getModelyear() < 2016) {
data.setRecentVehicle(false);
return data;
}
return null;
})
.filter(it -> it != null)
.collect(Collectors.toList())
);
List<Response> response = service.update(dataList, vehicle);
});
return dataList;
}
By simply replacing the for loop with a stream and substitute it with forEach is enough. We can also filter out the unwanted elements before forEach with filter.
Here's is how I would do it:
static void inJava8(List<Vehicle> vehicleList) {
Predicate<Vehicle> isBMW = vehicle -> "BMW".equalsIgnoreCase(vehicle.getName());
Predicate<Vehicle> isRecentvehicle = vehicle -> vehicle.getModelyear() > 2016;
Map<Boolean, List<Vehicle>> isRecentToVehicleMap = vehicleList.stream()
.filter(isBMW::test)
.collect(Collectors.partitioningBy(isRecentvehicle));
isRecentToVehicleMap.get(true).stream()
.forEach(vehicle -> processVehicle(vehicle, Test::recentBMWDataCOnstructor));
isRecentToVehicleMap.get(false).stream()
.forEach(vehicle -> processVehicle(vehicle, Test::notRecentBMWDataCOnstructor));
}
public static void processVehicle(Vehicle vehicle, Function<Driver, Data> function) {
// this predicate should be at class level to avoid creating per vehicle
Predicate<Driver> isAgeEligile = driver -> driver.getAge() > 18;
List<Data> dataList = vehicle.getDrivers()
.stream()
.filter(isAgeEligile::test)
.map(function::apply)
.collect(Collectors.toList());
service.update(dataList, vehicle);
}
public static Data recentBMWDataCOnstructor(Driver driver) {
Data data = new Data();
data.setDriverName(driver.getName());
data.setLuxuryVehicle(true);
data.setVehicleName("BMW");
data.setDriverEligible(true);
data.setRecentVehicle(false);
return data;
}
public static Data notRecentBMWDataCOnstructor(Driver driver) {
Data data = new Data();
data.setDriverName(driver.getName());
data.setLuxuryVehicle(true);
data.setVehicleName("BMW");
data.setDriverEligible(true);
data.setRecentVehicle(false);
return data;
}
final Vehicle[] vehicle = new Vehicle[1];
vehicleList.stream()
.filter(v -> "BMW".equalsIgnoreCase(v.getName()))
.map(v -> {vehicle[0] = v; return v;})
.flatMap(v -> v.getDrivers().stream())
.filter(driver -> driver.getAge() > 18)
.map(driver -> {
Data data = new Data();
data.setDriverName(driver.getName());
data.setLuxuryVehicle(true);
data.setVehicleName(vehicle[0].getName());
data.setDriverEligible(true);
data.setRecentVehicle(vehicle[0].getModelyear() > 2016);
return data;
})
.collect(Collectors.toList());

Using Java8 to find string values in one ArrayList present in another ArrayList using endsWith method

I have following code.
public class ComparingTwoLists {
public static void main(String[] args) {
List<String> small = Arrays.asList("AA", "BB");
List<String> big = Arrays.asList("a.b.AA", "a.b.BB", "a.b.CC");
List<String> list = new ArrayList<String>();
Consumer<String> consumer = (String outer) -> {
Stream<String> stream1 = small.stream();
Consumer<String> innerConsumer = (String inner) -> {
if (outer.endsWith(inner)) {
list.add(outer);
}
};
stream1.forEach(innerConsumer);
};
Stream<String> stream2 = big.stream();
stream2.forEach(consumer);
System.out.println(list);
}
}
I am trying to find out if string values in small is present in string values of big at end of string, using method endWith() method.
Is this efficient way of doing it?
Another problem I am facing in this code is that when I am trying to debug this code, I am able to view/inspect the value of inner, but cannot view the value of outer. Is there a way to check the value of outer while debugging.
You can do like this:
big.stream()
.filter(s -> small.stream().anyMatch(s::endsWith))
.collect(Collectors.toList());

Autowire all interfaces keyed by type

I've a an interface and there are multiple implementations of the interface. There can be only one type of implementation for each interface type and I want to collect all the interface implementations per type, i.e.
Map<String, InterfaceExample>
public interface InterfaceExample {
String getType();
ClassA getClassA();
}
If I had to get in this form Map<String, List<InterfaceExample>> I would have done in the following way:
#Autowired
private List<InterfaceExample> interfaceExamples;
#Bean
public Map<String, List<IntefaceExample>> getExamples() {
return interfaceExamples.stream()
.map(x -> new AbstractMap.SimpleEntry<>(x.getType(), x))
.collect(Collectors.groupingBy(Map.Entry::getKey, Collectors.mapping(Map.Entry::getValue, Collectors.toList())));
}
Now that I've to ensure there is only one implementation per type I can do in following way:
#Bean
public Map<String, IntefaceExample> getExamples() {
Map<String, List<IntefaceExample>> examples = interfaceExamples.stream()
.map(x -> new AbstractMap.SimpleEntry<>(x.getType(), x))
.collect(Collectors.groupingBy(Map.Entry::getKey, Collectors.mapping(Map.Entry::getValue, Collectors.toList())));
Map<String, InterfaceExample> output = new HashMap<>();
examples.forEach((key, value) -> {
if(value.size() > 1) {
throw new RuntimeException("Wrongly configured!! ");
} else if(value.size() == 1) {
output.put(key, value.get(0));
}
});
return output;
}
Is there a different way to ensure that there is only implementation per type and create the bean in a "streamified way" without explicitly creating the output map?
After groupingby you can check if there are multiple beans of same type and collect them into List
List<InterfaceExample> res = interfaceExamples.stream().collect(Collectors.groupingBy(InterfaceExample::getType)).values()
.stream().map(value -> {
if (value.size() == 1) {
return value.get(0);
}
throw new RuntimeException("Wrongly configured!! ");
}).collect(Collectors.toList());
The best way is write a custom method that does the validation logic
public InterfaceExample conditionCheck(List<InterfaceExample> value) {
if (value.size() == 1) {
return value.get(0);
}
throw new RuntimeException("Wrongly configured!! ");
}
And then simply use stream
List<InterfaceExample> res = interfaceExamples.stream()
.collect(Collectors.groupingBy(InterfaceExample::getType))
.values()
.stream()
.map(this::conditionCheck)
.collect(Collectors.toList());

Java 8 retrieve Map's values into an array with stream and filter

Would someone help me with getting the array of the map values with stream and filtering?
public class TheMap extends HashMap<String, String> {
public TheMap(String name, String title) {
super.put("name", name);
super.put("title", title);
}
public static void main(final String[] args) {
Map<Long, Map<String, String>>map = new HashMap<>();
map.put(0L, null);
map.put(1L, new TheMap("jane", "engineer"));
map.put(2L, new TheMap("john", "engineer"));
map.put(3L, new TheMap(null, "manager"));
map.put(4L, new TheMap("who", null));
map.put(5L, new TheMap(null, null));
}
}
The result that I am looking for is an ArrayList<TheMap> with only these two entries:
TheMap("jane", "engineer")
TheMap("john", "engineer")
Basically, retrieve TheMap with none-null name and title.
List<Map<String, String>> list =
map.values().stream().filter(v ->
v != null &&
!v.entrySet().isEmpty() &&
!v.containsValue(null)).
collect(Collectors.toList());
If you need an arrayList of TheMap, try the following way:
ArrayList<TheMap> as = map.values()
.stream()
.filter(v -> v != null && v.get("name") != null && v.get("title") != null)
.map(m -> (TheMap)m)
.collect(Collectors.toCollection(ArrayList::new)));

Resources