Java 8 apply list of functions on list of values - java-8

Task: we`ve got list of mappers, which must be applied on list of arguments.
How we can do?:
My not really good variant:
public static final Function<List<IntUnaryOperator>, UnaryOperator<List<Integer>>> multifunctionalMapper =
lst -> {
UnaryOperator<List<Integer>> uOp = new UnaryOperator<List<Integer>>() {
#Override
public List<Integer> apply(List<Integer> integers) {
final int[] curFunct = new int[1];
List<Integer> newLst = integers;
for (int i = 0; i < lst.size(); i++) {
curFunct[0] = i;
newLst = newLst.stream().map(curInt -> lst.get(curFunct[0]).applyAsInt(curInt)).collect(Collectors.toList());
}
return newLst;
}
};
return uOp;
};
list of mappers addOneMuxTwoTransformation:
public static final UnaryOperator<List<Integer>> addOneMuxTwoTransformation =
multifunctionalMapper.apply(Arrays.asList(x -> x+1, x -> x*2));
test:
addOneMuxTwoTransformation.apply(Arrays.asList(1,2,3)).stream().forEach(System.out::println);
will print:
4
6
8
How can be multifunctionalMapper's code reduced?

Is this what are you trying to do ?
List<IntUnaryOperator> ops = Arrays.asList(a -> a++, a -> a*2);
IntUnaryOperator reduce = ops.stream().reduce(a -> a, IntUnaryOperator::andThen);
IntStream.of(1, 2, 3).map(reduce).forEach(System.out::println);

next solution is
public static final Function<List<IntUnaryOperator>, UnaryOperator<List<Integer>>> multifunctionalMapper =lstFunc->
lstVals -> lstVals.stream()
.map(curValue -> lstFunc.stream().reduce(IntUnaryOperator::andThen).orElse(x -> x).applyAsInt(curValue))
.collect(Collectors.toList());

Related

Algorithm to remove duplicated location on list

I have a service to find journey and remove the duplicated visit city.
public static void main(String[] args){
List<List<String>> allPaths = new ArrayList<>();
allPaths.add(List.of("Newyork","Washington","Los Angeles","Chicago"));
allPaths.add(List.of("Newyork","Washington","Houston"));
allPaths.add(List.of("Newyork","Dallas"));
allPaths.add(List.of("Newyork","Columbus", "Chicago"));
Set<String> orphanageLocations = new HashSet<>();
removeDuplicatedLocation(allPaths, orphanageLocations);
//expected allPaths:
//"Newyork","Washington","Los Angeles","Chicago"
//"Newyork","Dallas"
//"Newyork","Columbus"
//expected orphanageLocations
//"Houston"
}
private static void removeDuplicatedLocation(List<List<String>> allPaths, Set<String> orphanageLocations){
//do something here
}
in the allPaths i store all the path from a origin to other cities.
but may be some path will contain same city, like Washington appear in both first and second path.
Now i need a service to remove that duplicated city. when two paths has same city then we take the path which going to more city.
And service also return city that can not visit. for example the 2nd path has "Washington" is duplicated with 1st path, then we remove that 2nd path (it has less city than first one), then there are no path to "Houston" available -> becoming orphanage
Other test cases:
public static void main(String[] args){
List<List<String>> allPaths = new ArrayList<>();
allPaths.add(List.of("Newyork","Washington","Los Angeles","Chicago", "Dallas"));
allPaths.add(List.of("Newyork","Los Angeles","Houston", "Philadenphia"));
allPaths.add(List.of("Newyork","Dallas"));
allPaths.add(List.of("Newyork","Columbus", "San Francisco"));
Set<String> orphanageLocations = new HashSet<>();
removeDuplicatedLocation(allPaths, orphanageLocations);
//expected allPaths:
//"Newyork","Washington","Los Angeles","Chicago", "Dallas"
//"Newyork","Columbus", "San Francisco"
//expected orphanageLocations
//"Houston","Philadenphia"
}
Would somebody suggest me a algorithm to solve it?
---Edit 1: i update my dirty solution here, still waiting for better one
private static void removeDuplicatedLocation(List<List<String>> allPaths, Set<String> orphanageLocations){
//sort to make sure the longest path is on top
List<List<String>> sortedPaths = allPaths.stream().sorted((a, b) -> Integer.compare(b.size(), a.size()))
.collect(Collectors.toList());
for(int i = 0; i < sortedPaths.size()-1; i++){
List<String> path = sortedPaths.get(i);
orphanageLocations.removeIf(path::contains);
for(int j = i+1; j < sortedPaths.size(); j++){
for(int k = 1; k < path.size();k++) {
Iterator<String> iterator = sortedPaths.get(j).iterator();
boolean isRemove = false;
while (iterator.hasNext()) {
String city = iterator.next();
if(isRemove && !path.contains(city)){
orphanageLocations.add(city);
}
if(StringUtils.equals(city, path.get(k))){
isRemove = true;
}
if(isRemove){
iterator.remove();
}
}
}
}
}
//remove path if it's only origin
sortedPaths.removeIf(item -> item.size() == 1);
allPaths.clear();
allPaths.addAll(sortedPaths);
}
---Edit 2: Thanks for solution of #devReddit, i made a small test with huge amount of route.
The more city in each path, the slower your solution is
public static void main(String[] args){
List<List<String>> allPaths = new ArrayList<>();
List<List<String>> allPaths2 = new ArrayList<>();
List<String> locations = Stream.of("A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N",
"O", "P", "Q", "R", "S", "T", "U", "V", "X", "Y", "Z").collect(Collectors.toList());
Random rand = new Random();
int numberOfRoute = 10000;
String origin = "NY";
for(int i = 0; i < numberOfRoute; i++){
List<String> route = new ArrayList<>();
List<String> route2 = new ArrayList<>();
route.add(origin);
route2.add(origin);
//int routeLength = rand.nextInt(locations.size());
int routeLength = 10;
while(route.size() < routeLength){
int randomIndex = rand.nextInt(locations.size()-1);
if(!route.contains(locations.get(randomIndex))){
route.add(locations.get(randomIndex));
route2.add(locations.get(randomIndex));
}
}
allPaths.add(route);
allPaths2.add(route2);
}
System.out.println("Process for " + allPaths2.size() + " routes");
Set<String> orphanageLocations2 = new HashSet<>();
long startTime2 = System.currentTimeMillis();
removeDuplicatedLocation3(allPaths2, orphanageLocations2); //uncle bob solution
long endTime2 = System.currentTimeMillis();
System.out.println(allPaths2);
System.out.println(orphanageLocations2);
System.out.println("Total time uncleBob solution(ms):" + (endTime2-startTime2));
System.out.println("Process for " + allPaths.size() + " routes");
Set<String> orphanageLocations = new HashSet<>();
long startTime = System.currentTimeMillis();
removeDuplicatedLocation(allPaths, orphanageLocations); //devReddit solution
long endTime = System.currentTimeMillis();
System.out.println(allPaths);
System.out.println(orphanageLocations);
System.out.println("Total time devReddit solution(ms):" + (endTime-startTime));
}
//devReddit solution
private static void removeDuplicatedLocation(List<List<String>> allPaths, Set<String> orphanageLocations) {
List<List<String>> sortedFixedPaths = allPaths // List.of produces immutable lists,
.stream() // so you can't directly remove string from the list
.sorted((a, b) -> Integer.compare(b.size(), a.size())) // this fixed list will be needed later
.collect(Collectors.toList());
List<List<String>> sortedPaths = sortedFixedPaths // The list is regenerated through manual deep copy
.stream() // generated a single string from the streams of
.map(path -> // each List<String> and created new list, this is now mutable
new ArrayList<>(Arrays.asList(String.join(",", path).split(","))))
.collect(Collectors.toList());
Set<List<String>> valuesToBeRemoved = new HashSet<>();
String source = sortedPaths.get(0).get(0);
Map<String, List<Integer>> cityMapOfIndex = generateHashMap(sortedPaths, source); // This hashmap keeps track of the existence of cities in different lists
removeDuplicates(cityMapOfIndex, sortedPaths); // this method removes the duplicates from the smaller paths
// adds the remaining cities to orphanList
cityMapOfIndex.forEach((cityName, value) -> { // this block checks whether any mid element in the path is gone
int index = value.get(0); // removes the path from result list
List<String> list = sortedPaths.get(index);
int indexInPath = list.indexOf(cityName);
if (indexInPath != sortedFixedPaths.get(index).indexOf(cityName)) {
orphanageLocations.add(cityName);
sortedPaths.get(index).remove(indexInPath);
}
});
valuesToBeRemoved.add(new ArrayList<>(Collections.singleton(source))); // To handle the case where only source remains in the path
sortedPaths.removeAll(valuesToBeRemoved); // after removing other duplicates
allPaths.clear();
allPaths.addAll(sortedPaths);
}
private static void removeDuplicates(Map<String, List<Integer>> cityMapOfIndex, List<List<String>> sortedPaths) {
for (Map.Entry<String, List<Integer>> entry : cityMapOfIndex.entrySet()) {
List<Integer> indexList = entry.getValue();
while (indexList.size() > 1) {
int index = indexList.get(indexList.size() - 1); // get the last index i.e. the smallest list of city where this entry exists
sortedPaths.get(index).remove(entry.getKey()); // remove the city from the list
indexList.remove((Integer) index); // update the index list of occurrence
}
cityMapOfIndex.put(entry.getKey(), indexList);
}
}
private static Map<String, List<Integer>> generateHashMap(List<List<String>> sortedPaths,
String source) {
Map<String, List<Integer>> cityMapOfIndex = new HashMap<>();
for (int x = 0; x < sortedPaths.size(); x++) {
int finalX = x;
sortedPaths.get(x)
.forEach(city -> {
if (!city.equalsIgnoreCase(source)) { // add entries for all except the source
List<Integer> indexList = cityMapOfIndex.containsKey(city) ? // checks whether there's already an entry
cityMapOfIndex.get(city) : new ArrayList<>(); // to avoid data loss due to overwriting
indexList.add(finalX); // adds the index of the List of string
cityMapOfIndex.put(city, indexList); // add or update the map with current indexList
}
});
}
return cityMapOfIndex;
}
//Bob solution
private static void removeDuplicatedLocation3(List<List<String>> allPaths, Set<String> orphanageLocations){
//sort to make sure the longest path is on top
List<List<String>> sortedPaths = allPaths.stream().sorted((a, b) -> Integer.compare(b.size(), a.size()))
.collect(Collectors.toList());
for(int i = 0; i < sortedPaths.size()-1; i++){
List<String> path = sortedPaths.get(i);
orphanageLocations.removeIf(path::contains);
for(int j = i+1; j < sortedPaths.size(); j++){
for(int k = 1; k < path.size();k++) {
Iterator<String> iterator = sortedPaths.get(j).iterator();
boolean isRemove = false;
while (iterator.hasNext()) {
String city = iterator.next();
if(isRemove && !path.contains(city)){
orphanageLocations.add(city);
}
if(StringUtils.equals(city, path.get(k))){
isRemove = true;
}
if(isRemove){
iterator.remove();
}
}
}
}
}
//remove path if it's only origin
sortedPaths.removeIf(item -> item.size() == 1);
allPaths.clear();
allPaths.addAll(sortedPaths);
}
Here is one of the result:
Test with route lenth is 6
Process for 10000 routes
[[NY, Q, Y, T, S, X], [NY, E], [NY, V, A, H, N], [NY, J, L, I], [NY, D], [NY, O], [NY, C], [NY, P, M], [NY, F], [NY, K], [NY, U], [NY, G], [NY, R], [NY, B]]
[]
Total time uncleBob solution(ms):326
Process for 10000 routes
[[NY, Q, Y, T, S, X], [NY, E], [NY, V], [NY, J, L], [NY, D], [NY, O]]
[A, B, C, F, G, H, I, K, M, N, P, R, U]
Total time devReddit solution(ms):206
With route length is 10
Process for 10000 routes
[[NY, J, V, G, A, I, B, R, U, S], [NY, L, X, Q, M, E], [NY, K], [NY, Y], [NY, F, P], [NY, N], [NY, H, D], [NY, T, O], [NY, C]]
[]
Total time uncleBob solution(ms):292
Process for 10000 routes
[[NY, J, V, G, A, I, B, R, U, S]]
[C, D, E, F, H, K, L, M, N, O, P, Q, T, X, Y]
Total time devReddit solution(ms):471
Also result is not the same,from the same inpit, mine return more valid route
Actually i this is not what i expect because solution from #devReddit look better & faster
Thanks
Your provided solution is O(m^2xn^2). I've figured out a solution which has O(n^2) time complexity. Necessary comments have been added as explanation:
The core method removeDuplicatedLocation:
private static void removeDuplicatedLocation(List<List<String>> allPaths, Set<String> orphanageLocations) {
List<List<String>> sortedFixedPaths = allPaths // List.of produces immutable lists,
.stream() // so you can't directly remove string from the list
.sorted((a, b) -> Integer.compare(b.size(), a.size())) // this fixed list will be needed later
.collect(Collectors.toList());
List<List<String>> sortedPaths = sortedFixedPaths // The list is regenerated through manual deep copy
.stream() // generated a single string from the streams of
.map(path -> // each List<String> and created new list, this is now mutable
new ArrayList<>(Arrays.asList(String.join(",", path).split(","))))
.collect(Collectors.toList());
Set<List<String>> valuesToBeRemoved = new HashSet<>();
String source = sortedPaths.get(0).get(0);
Map<String, List<Integer>> cityMapOfIndex = generateHashMap(sortedPaths, source); // This hashmap keeps track of the existence of cities in different lists
removeDuplicates(cityMapOfIndex, sortedPaths); // this method removes the duplicates from the smaller paths
cityMapOfIndex.entrySet().stream().forEach(city -> { // this block checks whether any mid element in the path is gone
String cityName = city.getKey(); // adds the remaining cities to orphanList
int index = city.getValue().get(0); // removes the path from result list
List<String> list = sortedPaths.get(index);
int indexInPath = list.indexOf(cityName);
if (indexInPath != sortedFixedPaths.get(index).indexOf(cityName)) {
orphanageLocations.add(cityName);
sortedPaths.get(index).remove(indexInPath);
}
});
valuesToBeRemoved.add(new ArrayList<>(Collections.singleton(source))); // To handle the case where only source remains in the path
sortedPaths.removeAll(valuesToBeRemoved); // after removing other duplicates
allPaths.clear();
allPaths.addAll(sortedPaths);
}
The removeDuplicates and generateHashMap methods used in the aforementioned stub is given below:
private static void removeDuplicates(Map<String, List<Integer>> cityMapOfIndex, List<List<String>> sortedPaths) {
for (Map.Entry<String, List<Integer>> entry : cityMapOfIndex.entrySet()) {
List<Integer> indexList = entry.getValue();
while (indexList.size() > 1) {
int index = indexList.get(indexList.size() - 1); // get the last index i.e. the smallest list of city where this entry exists
sortedPaths.get(index).remove(entry.getKey()); // remove the city from the list
indexList.remove((Integer) index); // update the index list of occurrence
}
cityMapOfIndex.put(entry.getKey(), indexList);
}
}
private static Map<String, List<Integer>> generateHashMap(List<List<String>> sortedPaths,
String source) {
Map<String, List<Integer>> cityMapOfIndex = new HashMap<>();
for (int x = 0; x < sortedPaths.size(); x++) {
int finalX = x;
sortedPaths.get(x)
.stream()
.forEach(city -> {
if (!city.equalsIgnoreCase(source)) { // add entries for all except the source
List<Integer> indexList = cityMapOfIndex.containsKey(city) ? // checks whether there's already an entry
cityMapOfIndex.get(city) : new ArrayList<>(); // to avoid data loss due to overwriting
indexList.add(finalX); // adds the index of the List of string
cityMapOfIndex.put(city, indexList); // add or update the map with current indexList
}
});
}
return cityMapOfIndex;
}
Please let me know if you have any query.

Print all possible course schedules algorithm

This is famous course schedule question, but want to print out all possible course schedules.
Q: There are ‘N’ courses, labeled from ‘0’ to ‘N-1’.
Each course can have some prerequisite courses which need to be completed before it can be scheduled.
Given the number of courses and a list of prerequisite pairs, write a method to print all possible ordering of courses meeting all prerequisites.
Assume that there is no cycle.
For the example in the main method, need to print
[3, 2, 0, 1]
[3, 2, 1, 0]
but my code prints only one of them
[3, 2, 1, 0]
Backtracking is needed to make it work, but at some point my backtracking is wrong and not sure how to fix this since it keeps choosing the same order after backtrack. Once chose 1, 0 and then backtrack, it should choose 0, 1, but keeps choosing the same order 1, 0.
Can someone help me to make it work?
class AllCourseOrders {
static Map<Integer, List<Integer>> map = null;
static int[] visited = null;
static int n = 0;
public static void printOrders(int courses, int[][] prerequisites) {
List<Integer> sortedOrder = new ArrayList<>();
// init
n = courses;
visited = new int[courses];
map = new HashMap<>();
for(int i =0; i < courses; i++)
map.put(i, new ArrayList<>());
// 1. build graph
for(int[] pre: prerequisites) {
int from = pre[0], to = pre[1];
List<Integer> list = map.get(from);
list.add(to);
}
// 2. dfs
List<List<Integer>> results = new ArrayList<List<Integer>>();
List<Integer> result = new ArrayList<>();
for(Integer u: map.keySet()) {
if(visited[u] == 0) {
dfs(u, result, results);
if(result.size() == n) {
results.add(new ArrayList<>(result));
result.remove(result.size()-1);
visited[u] = 0;
}
}
}
results.forEach(res -> System.out.println(res));
}
static void dfs(Integer u, List<Integer> result, List<List<Integer>> results) {
visited[u] = 1;
for(Integer v: map.get(u)) {
if(visited[v] == 0 ) {
dfs(v, result, results);
}
}
visited[u] = 2;
result.add(0, u);
}
public static void main(String[] args) {
printOrders(4, new int[][] { new int[] { 3, 2 }, new int[] { 3, 0 }, new int[] { 2, 0 }, new int[] { 2, 1 } });
}
}
Your algorithm finds the first solution it can, not every single one. Every time you are presented with multiple vertices to take next (you can take different starting nodes, certain class you can take in any order), each choice will lead to a different result.
The course problem is simply trying to topologically sort a directed, acyclic graph. GeeksForGeeks provides the algorithm on their site in java, where the vertices are the courses and the edges are the prereqs.

Iterate lists of different length using Java 8 streams

I am trying to iterate over three lists of different size but not getting the exact logic of how i can retrieve data from them and store in another list.
I was able to handle up to two list until I add some more filtration to the elements. For now I am using 3 for loops but i want to use Java 8 streams if possible. Can someone please suggest me the correct logic for the below iterations.
public class CustomDto {
public static void main(String... args) {
List<String> list1 = Arrays.asList("Hello", "World!");
List<String> list2 = Arrays.asList("Hi", "there");
List<String> list3 = Arrays.asList("Help Me");
Map<Integer, Object> map = new HashMap<>();
for (int i = 0; i < list1.size(); i++) {
List<String> list4 = new LinkedList();
for (int j = 0; j < list2.size(); j++) {
for (int k = 0; k < list3.size(); k++) {
if (!(list2.get(j).equals(list3.get(k))))
list4.add(list2.get(j));
}
if (j > list4.size() - 1) {
list4.add(null);
}
}
map.put(i, list4);
}
}
}
All i want to convert the above code into stream, in which i can iterate a list inside another list and can use the index of one another.
public static void main(String... args) {
List<String> list1 = Arrays.asList("Hello", "World!");
List<String> list2 = Arrays.asList("Hi", "there");
List<String> list3 = Arrays.asList("Help Me");
List<String> list4 = concat(list1, list2, list3); //you can add as many lists as you like here
System.out.println(list4);
}
private static List<String> concat(List<String>... lists) {
return Stream.of(lists)
.flatMap(List::stream)
.collect(Collectors.toList());
}
Output
[Hello, World!, Hi, there, Help Me]
Try this create a multiple dimension array out of List, from there you can use stream too
Customdto[][] listArray = new Customdto[][]{ l1.toArray(new Customdto[]{})
, l2.toArray(new Customdto[]{}), l3.toArray(new Customdto[]{})};
int size = listArray[0].length > listArray[1].length && listArray[0].length > listArray[2].length ?listArray[0].length
:(listArray[1].length > listArray[2].length ? listArray[1].length:listArray[2].length);
for(int i = 0; i <size;i++)
{
if(listArray[0].length >i && listArray[1].length >i && listArray[2].length >i &&
listArray[0][i].equals(listArray[1][i])
&& listArray[1][i].getCalendarDate().equals(listArray[2][i].getCalendarDate()))
{
l4.add(listArray[1][i]);
}else
{
l4.add(null);
}
}
Tried with Below Input
List<Customdto> l1 = new ArrayList<Customdto>();
List<Customdto> l2 = new ArrayList<Customdto>();
List<Customdto> l3 = new ArrayList<Customdto>();
List<Customdto> l4 = new ArrayList<Customdto>();
l1.add(new Customdto(1));
l1.add(new Customdto(2));
l1.add(new Customdto(3));
l1.add(new Customdto(4));
l2.add(new Customdto(1));
l2.add(new Customdto(2));
l2.add(new Customdto(3));
l3.add(new Customdto(1));
l3.add(new Customdto(2));
Output is
[Customdto [id=1], Customdto [id=2], null, null]

how to get multi properties's mean values using java8 stream?

there's one Object
#Data
class ScoreInfo{
String id;
float cove_score;
float theam_score;
float content_score;
float teach_score;
Date create_date;
ScoreInfoP scoreInfoP;
}
and ScoreInfoP is :
#Data
class ScoreInfoP{
String stream_sn;
String anchor_id;
String create_by;
}
sourceList is a list of ScoreInfo,I want to get cove_score,theam_score,content_score,teach_score's mean values,group by scoreInfoP property and return four mean values for each of these properties.
I can get only one mean value using such code:
Map<ScoreInfoP, Double> meanForCoveScore = sourceList.stream().collect(Collectors.groupingBy(ScoreInfo::getScoreInfoP,
Collectors.averagingDouble(ScoreInfo::getCove_score)));
I want to learn how to get four mean values using java8 or any easier way you suggest achieving this.
Waiting here for your generous help.
There isn't anything build-in for this, but it's not that complicated to build a custom Collector for that...
Map<String, List<Float>> result = Arrays.asList(first, second)
.stream()
.collect(Collectors.groupingBy(
x -> x.getScoreInfoP().getAnchorId(),
Collector.of(
() -> new float[5],
(a, x) -> {
a[0] += x.getCoveScore();
a[1] += x.getTheamScore();
a[2] += x.getTeachScore();
a[3] += x.getContentScore();
a[4]++;
},
(left, right) -> {
for (int i = 0; i < 4; ++i) {
left[i] += right[i];
}
return left;
}, x -> Arrays.asList(x[0] / x[4], x[1] / x[4], x[2] / x[4], x[3] / x[4]))
));
System.out.println(result);
I actually groupBy here on ScoreInfoP#anchorId; but you can do it on ScoreInfoP - for that you need to change x -> x.getScoreInfoP().getAnchorId() to x -> x.getScoreInfoP(). But obviously ScoreInfoP needs to override hashCode and equals.
As I said in the comment you should use a proper result class.
class ScoreInfoAverage {
private float cove_score;
private float theam_score;
private float content_score;
private float teach_score;
// ctor, getter, setter
}
Then you can use a custom Collector:
public static Collector<ScoreInfo, ?, ScoreInfoAverage> scoreInfoToAverage() {
class ScoreInfoAccumulator {
private DoubleSummaryStatistics cove_score = new DoubleSummaryStatistics();
private DoubleSummaryStatistics theam_score = new DoubleSummaryStatistics();
private DoubleSummaryStatistics content_score = new DoubleSummaryStatistics();
private DoubleSummaryStatistics teach_score = new DoubleSummaryStatistics();
public void add(ScoreInfo si) {
cove_score.accept(si.cove_score);
theam_score.accept(si.theam_score);
content_score.accept(si.content_score);
teach_score.accept(si.teach_score);
}
public ScoreInfoAccumulator combine(ScoreInfoAccumulator sia) {
cove_score.combine(sia.cove_score);
theam_score.combine(sia.theam_score);
content_score.combine(sia.content_score);
teach_score.combine(sia.teach_score);
return this;
}
public ScoreInfoAverage average() {
return new ScoreInfoAverage((float) cove_score.getAverage(),
(float) theam_score.getAverage(), (float) content_score.getAverage(),
(float) teach_score.getAverage());
}
}
return Collector.of(ScoreInfoAccumulator::new, ScoreInfoAccumulator::add,
ScoreInfoAccumulator::combine, ScoreInfoAccumulator::average);
}
Last but not least you add your Collector to the downstream:
Map<ScoreInfoP, ScoreInfoAverage> collect = scoreInfos.stream()
.collect(Collectors.groupingBy(ScoreInfo::getScoreInfoP, scoreInfoToAverage()));

HashMap manipulation using streams Java 8

Please let me know if there is a possibility of changing the below code in terms of Java 8 using parallel streams?
I am looking for an option to run the "outer for loop" in parallel and finally all the values of stationMap gets collected together?
Map<Integer, Set<Integer>> stationMap = new HashMap<>();
Map<Integer, Set<Integer>> routes = function();
for (Map.Entry<Integer, Set<Integer>> entry : routes.entrySet())
{
Set<Integer> stations = entry.getValue();
for (Integer station : stations) {
Set<Integer> temporaryStations = new HashSet<>(stations);
Set<Integer> stationSet = stationMap.get(station);
if (stationSet == null) {
stationSet = new HashSet<>();
temporaryStations.remove(station);
stationSet.addAll(temporaryStations);
stationMap.put(station, stationSet);
} else {
temporaryStations.remove(station);
stationSet.addAll(temporaryStations);
}
}
}
More shorter version:
routes.forEach((k, stations) -> {
stations.forEach((station) -> {
Set<Integer> stationSet = stationMap.get(station);
if (stationSet == null) {
stationSet = new HashSet<>();
stationSet.addAll(stations);
stationMap.put(station, stationSet);
} else {
stationSet.addAll(stations);
}
});
});
Even the long pre-Java 8 version can be simplified as there is no need to iterate over the entry set, when you are only processing the values and there is no need for code duplication within the two conditional branches:
Map<Integer, Set<Integer>> routes = function();
Map<Integer, Set<Integer>> stationMap = new HashMap<>();
for(Set<Integer> stations: routes.values()) {
for(Integer station: stations) {
Set<Integer> temporaryStations = new HashSet<>(stations);
temporaryStations.remove(station);
Set<Integer> stationSet = stationMap.get(station);
if (stationSet == null) {
stationMap.put(station, temporaryStations);
} else {
stationSet.addAll(temporaryStations);
}
}
}
using Java 8 features, you may get the improved variant:
routes.values().forEach(stations ->
stations.forEach(station -> {
Set<Integer> temporaryStations = new HashSet<>(stations);
temporaryStations.remove(station);
Set<Integer> old = stationMap.putIfAbsent(station, temporaryStations);
if(old!=null) old.addAll(stations);
})
);
though it might be simpler to first merge all values and remove the keys afterwards in one step:
routes.values().forEach(stations ->
stations.forEach(station ->
stationMap.computeIfAbsent(station, key -> new HashSet<>()).addAll(stations)
)
);
stationMap.forEach((k,set) -> set.remove(k));
It’s possible to formulate an equivalent (parallel) Stream operation:
Map<Integer, Set<Integer>> stationMap=routes.values().parallelStream()
.flatMap(stations -> stations.stream().map(station -> {
Set<Integer> temporaryStations = new HashSet<>(stations);
temporaryStations.remove(station);
return new AbstractMap.SimpleImmutableEntry<>(station, temporaryStations);
})
).collect(Collectors.toMap(
Map.Entry::getKey, Map.Entry::getValue, (a,b) -> {a.addAll(b); return a; }));
but this may also be simpler when removing the keys from the value set in a post processing step:
Map<Integer, Set<Integer>> stationMap=routes.values().parallelStream()
.flatMap(stations -> stations.stream().map(station ->
new AbstractMap.SimpleImmutableEntry<>(station, new HashSet<>(stations))
)
).collect(Collectors.toMap(
Map.Entry::getKey, Map.Entry::getValue, (a,b) -> {a.addAll(b); return a; }));
stationMap.entrySet().parallelStream().forEach(e -> e.getValue().remove(e.getKey()));
or you use a custom collector instead of flatMap:
Map<Integer, Set<Integer>> stationMap=routes.values().parallelStream()
.collect(HashMap::new,
(map,stations) -> stations.forEach(station ->
map.computeIfAbsent(station, key -> new HashSet<>()).addAll(stations)
),
(m1,m2) -> m2.forEach((k,v)->m1.merge(k, v, (a,b)->{a.addAll(b); return a;})));
stationMap.entrySet().parallelStream().forEach(e -> e.getValue().remove(e.getKey()));
this might be more efficient as it doesn’t need the temporary Map.Entry instances.

Resources