Java 8 filter by attribute - java

I have a class which is of the following definition
public class MyClass {
int val;
type t;
}
Where type is an enum with values A,B,C,D,....
I have a list of objects of MyClass and I want to filter out the first element of each type occurring in the list.
for example :-
Given list:
{{1,A},{2,A},{4,B},{5,B},{3,C}}
Output:
{{1,A},{4,B},{3,C}}
Is there a way to use filter() of a stream of the list to solve this problem?

I'm not sure if there's a way to do this with a single Stream pipeline, but you can do it with two.
The first pipeline groups the objects by the val property (producing a Map<Integer,List<MyClass>>) and the second takes the first object of each List produced by the first pipeline and collects them into the output List:
List<MyClass>
filtered = mycl.stream ()
.collect (Collectors.groupingBy (c -> c.val))
.values ()
.stream ()
.map (l -> l.get (0))
.collect (Collectors.toList ());

Here is a solution which is not as elegant I hoped for but it works:
Set<MyType> typeSet = new HashSet<>();
List<MyClass> result = list.stream()
.filter(c -> typeSet.add(c.getType())).collect(
Collectors.toList());

I'm not sure if there is any direct way of doing it but you can achieve it by doing
1) First use streams's findFirst method with filter (TypeOf type).
2) do above steps for all types.
3) Merge all above data into one list.

One of good way to achieve this override equals() and hashCode() in your MyClass class. Check equality on the basis of 'type'. Then put your List in Set it will remove all duplicate. :)

Related

Why is this Function<List<Person>,String> is not working?

I've working on Function and this I've already tried
Function<Person,String> byName = Person::getName;
System.out.println( byName.apply(list.get(1)) );
This worked and prints the Person's names at index 1;
But now I want to create Function<List<Person>,String> to iterate over all
the persons in the List
Function<List<Person>,String> allNames = a -> a.forEach(e-> e.getName());
System.out.println(allNames.apply(list));
Its throws an error as "void cannot be converted to String "
forEach() takes a consumer which applies a function to each element. It doesn't return anything, so you can't use it inside of a System.out.println() call. If you feel like you want to return something from a forEach(), chances are you want to stream() if necessary, then call map() instead.
However, staying closer to your first example, you likely want to call forEach() on your list instead, and then print out the result of the function on each elment there:
Function<Person, String> byName = Person::getName;
list.forEach(e -> System.out.println(byName.apply(e.getName())));
Function package has this Two Functional Interface
Supplier
Consumer
According to Documentation ->
Use Supplier if it takes nothing, but returns something.
Use Consumer if it takes something, but returns nothing.
This following snippet solved my Problem ->
Consumer<List<Person>> allNames = (a) -> a.forEach(e -> System.out.print(e.getName()+" "));
allNames.accept(list);

Intersection of Two Lists Objects in java 8

An intersection of Two Lists Objects in java 8. Can some tell me what am I doing wrong?
List<Student> originalStudent = new ArrayList<>();
List<Student> newStudent = new ArrayList<>();
List<Student> intersectListStudent = new LinkedList<>()
originalStudent.add(new Student("William", "Tyndale",1));
originalStudent.add(new Student("Jonathan", "Edwards",2));
originalStudent.add(new Student("Martin", "Luther"),3);
newStudent.add(new Student("Jonathan", "Edwards",2));
newStudent.add(new Student("James", "Tyndale",4));
newStudent.add(new Student("Roger", "Moore",5));
originalStudent.forEach(n ->
newStudent.stream()
.filter(db -> !n.getName().equals(db.getName()) &&
!n.getLastName().equals(db.getLastName()))
.forEach(student-> intersectListStudent .add(student)));
Can some tell me what am I doing wrong?
You violate the Side-effects principle of java-stream which in a nutshell says that a stream shouldn't modify another collection while performing the actions through the pipelines. I haven't tested your code, however, this is not a way you should treat streams.
How to do it better?
Simply use the List::contains in the filter's predicate to get rid of the unique values.
List<Student> students = originalStudent.stream()
.filter(newStudent::contains)
.collect(Collectors.toList());
This solution (understand the method List::contains) is based on the implemented equality comparison using Object::equals. Hence, there is needed to override the very same method in the class Student.
Edit: Please, be aware that that automatically overriding the Object::equals will mind the id to the equality computation. Therefore the equality will be based on the name and surname only. (thanks to #nullpointer).
Without the Object::equals overridden?
You have to perform the comparison in the filter using another stream and the method Stream::anyMatch which returns true if the predicate is qualified.
List<Student> students = originalStudent.stream()
.filter(os -> newStudent.stream() // filter
.anyMatch(ns -> // compare both
os.getName().equals(ns.getName() && // name
os.getLastName().equals(ns.getLastName()))) // last name
.collect(Collectors.toList());
What you can do is construct a SortedSet<Student> from the two concatenated lists originalStudent and newStudent. The sorted set uses a Comparator.comparing(Student::getName).thenComparing(Student::getLastName) as its comparator.
Stream.concat(originalStudent.stream(), newStudent.stream())
.collect(Collectors.toCollection(() -> new TreeSet<>(
Comparator.comparing(Student::getFname)
.thenComparing(Student::getLname))
))

Creating an ArrayList from a method which returns a String

I have an custom class InfoAQ which has a method called public String getSeqInf(). Now I have an ArrayList<InfoAQ> infList and
I need an ArrayList<String>strList = new ArrayList<String>with the content from getSeqInf()for each element.
This is the way Im doing it right now ...
for(InfoAQ currentInf : infList)
strList.add(currentInf.getSeqInf());
Is there an alternative way to make it ? Maybe a faster one or one liner ?
Yes, there is:
strList = infList.stream().map(e -> g.getSeqInf()).collect(Collectors.toList());
The map step can be also written in another way:
strList = infList.stream().map(InfoAQ::getSeqInf).collect(Collectors.toList());
which is know as method reference passing. Those two solutions are equivalent.
Also maybe this one:
List<String> strList = new ArrayList<String>();
infList.forEach(e -> strList.add(e.getSeqInf()));
And there is another one (-liner, if you format it in a single line):
infList.forEach(currentInf -> {strList.add(currentInf.getSeqInf());});
while I would prefer a formatting in more lines:
infList.forEach(currentInf -> {
strList.add(currentInf.getSeqInf());
});
Using streams
infList.stream()
.map(InfoAQ::getSeqInf)
.collect(Collectors.toCollection(ArrayList::new))
Using Collectors.toCollection here to create an ArrayList that will hold the results as you do in your case. (Important if you do care about the result list type as Collectors.toList() does not guarantee this)
May not be the fastest as using stream has some overhead. You need to measure/benchmark to find out its performance
This code will iterate all the data in the list, as getSeqInf returns a String, the collect method will store all returns of the getSeqInf method in a list.
`List listString = infList.stream().map(InfoAQ::getSeqInf).collect(Collectors.toList());`
or
`
ArrayList<String> listString = new ArrayList<>();
for(int i = 0; i < infoAq.size(); i++) {
listString.add(infoAq.get(i).getSeqInf());
}`

Java Streams - Sort if Comparator exists

I have a class where optionally a Comparator can be specified.
Since the Comparator is optional, I have to evaluate its presence and execute the same stream code, either with sorted() or without:
if(comparator != null) {
[...].stream().map()[...].sorted(comparator)[...];
} else {
[...].stream().map()[...];
}
Question:
Is there a more elegant way to do this without the code duplication?
Note:
A default Comparator is not an option, I just want to keep the original order of the values I am streaming.
Besides, the elements are already mapped at the point of sorting, so I can not somehow reference the root list of the stream, as I do not have the original elements anymore.
You can do something like this:
Stream<Something> stream = [...].stream().map()[...]; // preliminary processing
if(comparator != null) {
stream = stream.sorted(comparator); // optional sorting
}
stream... // resumed processing, which ends in some terminal operation (such as collect)
Another way would be to use Optional:
Stream<Whatever> stream = [...].stream().map()[...];
List<WhateverElse> result = Optional.ofNullable(comparator)
.map(stream::sorted)
.orElse(stream)
.[...] // <-- go on with the stream pipeline
.collect(Collectors.toList());
You could define a comparator of your type (I used E as a placeholder here) that will not change the order:
Comparator<E> NO_SORTING = (one, other) -> 0;
If the comparator field is an Optional of Comparator, you can then use
.sorted(comparator.orElse(NO_SORTING))
If you don't mind use third party library StreamEx
StreamEx(source).[...].chain(s -> comparator == null ? s : s.sorted(comparator)).[...];
You can accomplish this using an auxiliary function.
static <T, R> R applyFunction(T obj, Function<T, R> f) {
return f.apply(obj);
}
and
applyFunction([...].stream().map()[...],
stream -> comparator == null ? stream : stream.sorted(comparator))
[...];
You don't need to know intermediate stream type.

Java use void method for stream mapping?

Let's say I have a void method that just does transformation on an object, without returning any value, and I want to use it in a context of a stream map() function, like this:
public List<MyObject> getList(){
List<MyObject> objList = ...
return objList.stream().map(e -> transform(e, e.getUuid())).collect(Collectors.toList());
}
private void transform(MyObject obj, String value){
obj.setUuid("prefix" + value);
}
The example is made up for simplicity - the actual method is doing something else than just mucking up the UUID of an object.
Anyway, how is that possible to use a void method in a scenario like the above?
Surely, I could make the method return the transformed object, but that's besides the point and is violating the design (the method should be void).
Seems like this is a case of forced usage of java 8 stream. Instead you can achieve it with forEach.
List<MyObject> objList = ...
objList.forEach(e -> transform(e, e.getUuid()));
return objList;
In addition to Eugene's answer you could use Stream::map like this:
objList.stream()
.map(e -> {
transform(e, e.getUuid());
return e;
}).collect(Collectors.toList());
Actually, you don't want to transform your current elements and collect it into a new List.
Instead, you want to apply a method for each entry in your List.
Therefore you should use Collection::forEach and return the List.
List<MyObject> objList = ...;
objList.forEach(e -> transform(e, e.getUuid()));
return objList;
If you are sure that this is what you want to do, then use peek instead of map
You can use the peek(Consumer<? super T> action) method. It takes a consumer as a parameter, performs an action on each of the elements and returns a stream.
objList.stream().peek(e -> transform(e,e.getUuid())).collect(Collectors.toList());

Categories

Resources