Add objects in a list using java 8 - java

I am new to Java 8, so I have a list of CollegeGroup and I have a Student that has many groups. Each group has a CollegeGroupMember who is linked to the student. Is there any way to return these groups right in my list(studentCollegeGroups)?
final List<CollegeGroup> studentCollegeGroups = new ArrayList<>();
student.getCollegeGroupMembers().forEach(collegeGroupMember ->
studentCollegeGroups.add(collegeGroupMember.getCollegeGroup()));

The Java 8 best practice is not to add things to an outside list, but to create the whole list as the result of one stream expression. Here, it'd probably be
return student.getCollegeGroupMembers().stream()
.map(CollegeGroupMember::getCollegeGroup)
.collect(Collectors.toList());

Related

what is the best way to divide a list into two list based on a criteria [duplicate]

This question already has answers here:
Java Stream: divide into two lists by boolean predicate
(5 answers)
Closed 4 years ago.
I have a list of objects and I want to divide this list into two lists based on the value of a field. Here's how I did it with Stream.filter():
public void setMovieMedia(List<MovieMediaResponse> movieMedias) {
// fill the first list
this.setPhotos(movieMedias
.stream()
.filter(movieMedia -> movieMedia.getType().equals(MediaType.PHOTO))
.collect(Collectors.toList()));
// fill the second list
this.setVideos(movieMedias
.stream()
.filter(movieMedia -> movieMedia.getType().equals(MediaType.VIDEO))
.collect(Collectors.toList()));
}
However, with this approach, I think I am looping through the list twice. Is there a way to do achieve the same thing without iterating through the list twice?
PS: I know I can achieve this by using List.forEach() like in the following example, but I'd like to avoid using this method:
List<MovieMediaResponse> movieMedias = ...;
movieMedias.forEach(m -> {
if (m.getType().equals(MediaType.PHOTO))
// append to list1
else if (m.getType().equals(MediaType.VIDEO))
// append to list2
});
What you are looking for is Collectors.partitioningBy that will get you a Map where key is a Boolean; thus you can do:
result.get(true/false)
to get each individual list. Since you seem to know your way around streams, I will not show an example, you can most probably figure it out

Alternative for filtering out only first element of a list that matches some element

I am trying to think of an alternative to the List method for remove(int index) and remove(T element). Where I take a list and do some filtering and return a new list without the element requested to be removed. I want to do it functionally, as I don't want to mutate the original list.
Here is my attempt.
List<Integer> integers = Arrays.asList(2, 4, 1, 2, 5, 1);
Integer elementToRemove = 1;
List<Integer> collect =
integers.stream()
.filter(elements -> !elements.equals(elementToRemove))
.collect(Collectors.toList());
This will remove all the 1's.
I wont to remove just the first 1, so I will be left with a list like [2,4,2,5,1]
I know how to do it using indexOf() and sublist() and addAll(). But I feel this is not as good as using streams.
Looking for functional solutions using streams for implementing remove(int index) and remove(T element).
I want to do it functionally, as I dont want to mutate the original
list.
You can still perform the removal operation and not mutate the source without going functional.
But I feel this is not as good as using streams.
Quite the opposite as this is done better without streams:
List<Integer> result = new ArrayList<>(integers); // copy the integers list
result.remove(Integer.valueOf(elementToRemove)); // remove from the new list leaving the old list unmodified
I agree with #Aominè but this can be a alternative in stream API
IntStream.range(0,integers.size())
.filter(i->i != integers.indexOf(elementToRemove))
.mapToObj(i->integers.get(i))
.collect(Collectors.toList());
As #Aominè commented for optimize, find index of elementToRemove firstly then use it in the filter.
While my other answer is definitely the way I recommend to proceed with and #Hadi has also provided the "stream" alternative which is also valid. I decided to play about with different ways to achieve the same result using features as of JDK-8.
In JDK-9 there is a takeWhile and dropWhile methods where the former returns a stream consisting of the longest prefix of elements taken from a stream that match a given predicate.
The latter returns a stream consisting of the remaining elements of a given stream after dropping the longest prefix of elements that match a given predicate.
The idea here is to consume the elements while it's not equal to the elementToRemove:
integers.stream()
.takeWhile(e -> !Objects.equals(e, elementToRemove))
and drop the elements while it's not equal to the elementToRemove and skip(1) to exclude the elementToRemove:
integers.stream()
.dropWhile(e -> !Objects.equals(e, elementToRemove))
.skip(1)
hence yielding two streams where the first stream is all the preceding numbers to elementToRemove and the second stream plus the skip(1) is all the elements after the elementToRemove then we simply concatenate them and collect to a list implementation.
List<Integer> result = Stream.concat(integers.stream()
.takeWhile(e -> !Objects.equals(e, elementToRemove)),
integers.stream()
.dropWhile(e -> !Objects.equals(e, elementToRemove))
.skip(1))
.collect(Collectors.toList());
Assuming the element to remove does not exist in the list the takeWhile will consume all the elements and the dropWhile will drop all the elements and when we merge these two streams we get back the initial elements.
Overall this will accomplish the same result as the other answers.
However, do not use this solution in production code as it's suboptimal and not obvious to the eye what the code does. it's only here to show different ways to accomplish the said requirement.

How to work with Java 8 streams?

I want to know how to work with Java 8 streams and how to use the different kind of available stream operations.
For example, I wrote this part of code:
ArrayList<State> toBeRemoved = new ArrayList<>();
for (State s : newStates)
if (path.contains(s)) // path is a stack of State
toBeRemoved.add(s);
for (State s : toBeRemoved)
newStates.remove(s);
I want to rewrite it using java 8 stream api calls. How can I do it?
No need for a stream here, you can use the new Collection#removeIf method:
newStates.removeIf(path::contains);
Or, if path is a Collection:
newStates.removeAll(path);
In this case, you can simply produce an output List containing only the States that should be retained, and assign that List to the newStates variable :
newStates = newStates.stream()
.filter(s -> !path.contains(s))
.collect(Collectors.toList());
The filter keeps only States for which path.contains(s) returns false.
Of course, if newStates is originally initialized as a copy of some "oldStates" List, you can skip that initilization step and use the original "oldStates" List as input.

How to delete similar elements from String array or ArrayList [duplicate]

This question already has answers here:
How to get unique values from array
(13 answers)
Closed 7 years ago.
I'm totally new to programming and I have String array:
String dates[] = {"01-01-1993", "19-11-1993", "01-01-1993", "03-03-2000", "03-03-2000"};
In the above array dates[0] == dates[2] and dates[3] == dates[4], I want to delete the duplicate values which IS REPEATED and I want program to produce result like this:
dates[] = {"01-01-1993", "19-11-1993", "03-03-2000"}
Some are using ArrayList concept some are using complex for loops and I'm confused, so could you please help in achieving the above task.
Thanks In Advance.
You can use a Set to remove duplicates.
Set<T> dateSet = new HashSet<T>(Arrays.asList(dates));
dateSet will only contain unique values.
If you need to get back to an array
dates = dateSet.toArray(new String[dateSet.size()]);
Your question raises a few points:
In general, this kind of manipulation of collections is much easier and cleaner if you use the Collection classes (Set, List, etc). For example, a Set guarantees uniqueness of the elements:
String dates[] = {"01-01-1993", "19-11-1993","01-01-1993","03-03-2000","03-03-2000"};
Set<String> uniqueDates = new HashSet<>(Arrays.asList(dates));
But HashSet is unordered; if you iterate over uniqueDates the order in which you get the back is arbitrary. If the order is important, you could use LinkedHashSet:
Set<String> orderedUniqueDates = new LinkedHashSet<>(Arrays.asList(dates));
The basic collection that come with Java are pretty good; but if you use Google's Guava library (https://github.com/google/guava), it gets really nice and you can do really powerful things like finding intersections of sets, set differences etc.:
Set<String> uniqueDates = Sets.newHashSet(dates);
More fundamentally, String is a very poor choice of data type to represent Dates. Consider using either java.util.Date or (if using a Java version prior to 8) org.joda.time.DateTime (http://www.joda.org/joda-time/). Not only will this give you type safety and ensure that your data is really all dates, but you could then do things like sorting.
Using Java 8 you can do the following:
String dates[] = {"01-01-1993", "19-11-1993", "01-01-1993", "03-03-2000", "03-03-2000"};
dates = Arrays.stream(dates).distinct().toArray(String[]::new);
this will work simply
Set<T> tempSet= new HashSet<T>(Arrays.asList(dates));
String[] dates= tempSet.toArray(new String[tempSet.size()]);//copy distinct values

How to print two lists together using Stream API java 8?

I have two lists as follow
List<String> names = Arrays.asList("James","John","Fred");
List<Integer> ages = Arrays.asList(25,35,15);
What i want to do is to print those two lists like so
James:25
John:35
Fred:15
It is easy to do it using the classic way
for(int i=0;i<names.size();i++){
System.out.println(names.get(i)+":"+ages.get(i));
}
Is there a way to do it using Stream API java 8?
What i am able to do is to print only one single list
names.stream().forEach(System.out::println);
The easiest way is to create an IntStream to generate the indices, and then map each index to the String you want to create.
IntStream.range(0, Math.min(names.size(), ages.size()))
.mapToObj(i -> names.get(i)+":"+ages.get(i))
.forEach(System.out::println);
Also you might be interested in this SO question Zipping streams using JDK8 with lambda (java.util.stream.Streams.zip), because this is the kind of functionality you're asking for.
My StreamEx library has some syntactic sugar for this case:
StreamEx.zip(names, ages, (name, age) -> name+":"+age).forEach(System.out::println)
Basically inside it's the same as in accepted answer. The only difference is that IllegalArgumentException will be thrown if size of lists differs.
While Alexis C. answer is correct, one would argue it is the easiest way to achieve requested behavior in Java 8. I propose:
int[] i = {0};
names.forEach(name -> {
System.out.println(name + ages.get(i[0]++));
});
Or even without index:
List<Integer> agesCopy = new ArrayList<Integer>(ages);
names.forEach(name -> {
System.out.println(name + agesCopy.remove(0));
});

Categories