Java use void method for stream mapping? - java

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());

Related

How to check a collection is not null and elements in collection are also not null

I get is null using apache collections utils in java 8:
if (CollectionUtils.isNotEmpty(reportEnvelopeApps)) {
}
But if the collection reportEnvelopeApps contains one element null, It works unexpectedly. So I have to write code like this:
if (reportEnvelopeApps == null) {
return null;
}
reportEnvelopeApps.removeAll(Collections.singleton(null));
if (CollectionUtils.isEmpty(reportEnvelopeApps)) {
return null;
}
What is the better way to avoid bad code like this?
You can use Optional and return list with only non null elements or else return null
List<String> res = Optional.ofNullable(reportEnvelopeApps)
.map(list->list.removeIf(Objects::isNull) ? list : list)
.filter(list->!list.isEmpty())
.orElse(null);
You can simply wrap this in a utility method
public (static) List<String> removeNull(List<String> orig){
if(orig==null){
return Collections.emptyList();
}
return orig.stream.filter(x->x!=null).collect(Collectors.toList);
}
You can put that in your own CollectionUtil/ListUtil class or somewhere else where you can easily re-use it. Your main code then just applies the filter method and checks whether the list has any values. If at all necessary - at best you simply have code that iterates over the list, making special case handling for null/empty list obsolete.
If you really care for speed, you might just hand around streams instead of collections, that would avoid the copy that is being created here, but in most cases that is a non-issue.
You could collect all possible non-null elements to a new list, using Stream, after CollectionUtils.isNotEmpty(reportEnvelopeApps)
if (CollectionUtils.isNotEmpty(reportEnvelopeApps)) {
List<ReportEnvelope> nonNullList = reportEnvelopeApps
.stream()
.filter(Objects::nonNull)
.collect(Collectors.toList());
// Proceed with whatever needs to be done with the new list.
}

How to sort list based on nested object property

I am new to Java streams and I just want to sort the keys for my object.
So, I try something like this and it works
List<FooSelect> li= Arrays.stream(obj.getFoos().getFoo()) //Stream<Foo>
.map(Foo::getSelect) //Stream<FooSelect>
.sorted(Comparator.comparing(FooSelect::getFoosKey)) //Stream<FooSelect>
.collect(Collectors.toList());
This sorts it according to what I want.
But the result I get is in List<FooSelect> object, though I want it in List<Foo>.
How can I change the mapping after it is sorted?
I want to again change the response in
//Stream<Foo> after it is sorted.
I want something like
List<Foo> li = same result of code above;
Class : FooSelect
just has some String fields
string FooKey
string FooTKey
and getters and setters for that (one of them is getFoosKey by which I am sorting)
Class: Foo
private FooSelect select
private FooInsert insert
Foo(select, insert)
public FooSelect getSelect() {
return select; }
Same way setter.
Remove the map. The map changes the object in the stream. Update the sorted statement as
.sorted(Comparator.comparing(f -> f.getSelect().getFoosKey()))
You can use lambda expression in Comparator.comparing instead of method reference
List<Foo> res = foos.stream()
.sorted(Comparator.comparing(fo->fo.getSelect().getFooKey()))
.collect(Collectors.toList());
For just sorting you don't even need stream
foos.sort(Comparator.comparing(fo->fo.getSelect().getFooKey()));

Java 8 filter by attribute

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. :)

How to use Stream instead for each loop

I have a loop which update an String object:
String result = "";
for (SomeObject obj: someObjectList) {
result = someMetohd(obj, result);
}
An implementation of someMethod is irrelevant:
private String someMethod(SomeObject obj, String result) {
result = result.concat(obj.toString());
return result;
}
And I want to use Stream instead a loop. How to implement it with Stream?
#SuppressWarnings("OptionalGetWithoutIsPresent")
String result = Stream.concat(Stream.of(""), someObjectList.stream())
.reduce(this::someMethod)
.get();
Your someMethod should be associative as specified in the documentation, however this is only important for parallel streams, while your code is explicitly sequential
As you always add to the result, you can consider it a first element of the stream and then use reduce method which will always merge first two elements - current result and next element
result has to be the first parameter of your someMethod
Because all elements in the stream have to be of the same type, while you have String result and SomeObject elements, you need to change the signature of someMethod to accept two Objects (and do the casts inside the method): private String someMethod(Object result, Object obj). This is the most ugly part of this solution.
You can inline the initial value of the result - no need to define result upfront
You might want to change this::someMethod depending on where this method is declared
Finally, you don't need to worry about handling Optional result, because the stream always has at least one element so it's safe to just call get()
final StringBuilder resultBuilder = new StringBuilder();
someObjectList.stream().map(SomeObject::toString).forEach(resultBuilder::append);
final String result = resultBuilder.toString();
To know more about Streams, you can check this page: http://winterbe.com/posts/2014/07/31/java8-stream-tutorial-examples/, I think it's very helpful.
Although the functional equivalent of what you're trying to achieve here is possible with streams, it's worth reminding you that functional and iterative ways of thinking are not necessarily compatible.
Generally you think of each element on its own, and you don't have visibility over other elements, unless you're using a special function like reduce.
Here's something that does what you've asked for:
final List<Object> objectList = Arrays.asList("a", "b", "c", "d");
String concatString = objectList.stream()
.map(e -> e.toString())
.reduce((result, element) -> result.concat(e))
.get();
Map turns the entire stream into a list, but with the toString function called separately on every element. Reduce is more complex. It can be described as an accumulation. It executes a function between the result, and the current element. In this case, it takes the first element, and concatenates it to the second. It then takes the first/second concatenation, and applies the same function to the third. And so on.
Instead of dealing with lambdas, you can also pass in methods directly, to tighten up your code a bit:
String result = objectList.stream()
.map(Object::toString)
.reduce(String::concat)
.get();

Java 8 Stream get object from filter result

Note: I don't know if I titled this correctly, so please feel free to alter it to something more appropriate, I'm quite new to the terminology of Java 8.
Question: I have some object A, I want to filter it based on a numerical value that it holds, say, an integer. I want to find the object with the highest value and then return that Object. How is this done using streams?
public SomeObject getObjectWithHighestValue()
{
int max = Integer.MIN_VALUE;
SomeObject maxObj = null;
for(SomeObject someObj : someList)
{
if(someObj.getValue() > max)
{
max = someObj.getValue();
maxObj = someObj;
}
}
return maxObj;
}
Above I have included a java 7 way of doing roughly what I want.
There's not necessarily a need for streams, you could also use Collections.max with a custom comparator:
import static java.util.Collections.max;
import static java.util.Comparator.comparing;
...
SomeObject o = max(someList, comparing(SomeObject::getValue));
The advantages with the stream approach is that you can parallelize the task if needed, and you get back an empty Optional if the list is empty (whereas it would throw an exception with an empty list using Collections.max, but you can check the size before).
return list.stream()
.max(Comparator.comparing(SomeObject::getValue))
.orElse(null);
SomeObject maxObject = someList.stream().max(Comparator.comparing(SomeObject::getValue).get();

Categories

Resources