So I'm new to Java8. I've read about streams, but most examples are very basic. I was wondering how it'd be done with nested objects. Here is an example from my code:
for (Gas gas : gases) {
resourceNodes.add(gas.getRefinery().unit());
}
It seems like I should be able to one-line this with a stream, but I can't quite figure it out. Could someone provide an answer with a stream. Is there a way to use :: syntax with nested methods too?
Edit:
To clarify the example, getRefinery() returns an object of type: UnitInPool, whose method unit() returns an object of type: Unit. resourceNodes is an ArrayList of Unit.
The :: syntax that you refer to is what's known as a method reference.
Assuming resourceNodes is unassigned (or empty, in which case you can remove any previous assignment) prior to the for-loop, then you'd want to first map each Gas to whatever type unit() returns, and then collect the Stream to a List:
resourceNodes = gases.stream()
.map(Gas::getRefinery)
.map(GasRefinery::unit)
.collect(Collectors.toList());
Otherwise, if your goal is to simply add to resourceNodes, then it would be very similar:
resourceNodes.addAll(gases.stream()
.map(Gas::getRefinery)
.map(GasRefinery::unit)
.collect(Collectors.toList()));
You need to provide more code for a reasonable answer, but I'm guessing you can get a stream of units, whetever these are (and whatever getRefinery returns) this way:
gases.stream().map(Gas::getRefinery).map(???::unit)
then you can for example collect the result with collect(Collectors.toList()) and just call resourceNodes.addAll with the collected result as parameter
resourceNodes = gases.stream().map(gas -> gas.getRefinery().unit()).collect(Collectors.toList());
If you want only method references, you can use this:
gases.stream().map(Gas::getRefinery).map(UnitInPool::unit).map(resourceNodes::add);
or
gases.stream().map(Gas::getRefinery).map(UnitInPool::unit).forEach(resourceNodes::add);
Otherwise, a lambda would likely be better since it's a lot shorter and more readable, and works, when you have methods that take multiple parameters or need to do multiple complex operations.
gases.stream().forEach(g -> resourceNodes.add(g.getRefinery().unit()));
This is basically the same as your previous code, but I would suggest the for-loop.
Welcome to the SO community. I hope the following helps.
List<Unit> resourceNodes = gases.stream() // open a stream
.map(gas -> gas.getRefinery()) // convert to UnitInPool
.filter(unitInPool -> Objects.nonNull(unitInPool)) // null check to avoid NPE
.map(unip -> unip.getUnit()) // convert to Unit
.collect(Collectors.toList()) // collect all the values in a List
Related
I have the following code
//assume we have a list of custom type "details" already constructed
for(int i = 0; i < details.size(); ++i) {
CallerID number = details.get(i).getNextNumber();
ClientData.addToClient(number);
}
I have oversimplified the code. The enum CallerID and the ClientData object work as intended. I am asking for help converting this loop to a lambda function so I can understand the logic of how to do so, then fill in the appropriate code as needed.
Let's first write it as a modern basic for loop and golf it a bit, just so we're comparing apples to apples:
for (var detail : details) clientData.addToClient(detail.getNextNumber());
And this is probably the right answer. It is local var, exception, and control flow transparent (which is what you want), and short.
The lambda form is this, but it's got downsides (mostly, those transparencies). It also isn't any shorter. You shouldn't write it this way.
details.stream().forEach(d -> clientData.addToClient(detail.getNextNumber());
You may be able to just remove stream() from that. But probably not.
Generally when people say "I want it in lambda form", that's not because someone is holding a gun to your head - you are saying that because somebody peddling a religion of sorts to you told you that 'it was better' and that this 'will scale'. Realize that they are full of it. There can be advantages to 'functional style', but none of these snippets are functional. A true functional style would involve a bunch of side-effect-free transformations, and then returning something.
.addToClient? You've lost the functional game there - you would want to instead convert each detail to something (presumably a ClientID), and from there construct an immutable object from that stream. You'd 'collect' your ClientIDs into a clientData object.
Let's say for example that clientData is just a 'list of ClientIDs' and nothing more. Then you'd write something like this:
var clientData = details.stream()
.map(MyDetailClass::getNextNumber)
.collect(Collectors.toList());
Is this better? No. However, if you're looking for 'a stream-style, lambda-based functional take on things', that qualifies. The output is constructed by way of collection (and not forEach that does a side-effect operation), and all elements involved are (or can be) immutable.
There's no particular reason why you'd want this, but if for some reason you're convinced this is better, now you know what you want to do. "Just replace it with a lambda" doesn't make it 'functional'.
I am asking for help converting this loop to a lambda function so I can understand the logic of how to do so, then fill in the appropriate code as needed.
A Function returns a value. As you are just updating something what you need is a Consumer which accepts a single argument of a list of some detail. Assuming those are in a Class named SomeDetails, here is how you would do it.
As you iterating over some structure limited by size and using get(i) I am presuming a list is required here.
List<SomeDetails> details = new ArrayList<>(); // then populated
// lambda definition
Consumer<List<SomeDetails>> update = (lst)-> {
for(SomeDetails detail : lst) {
CallerID number = detail.getNextNumber();
ClientData.addToClient(number);
}
};
And then invoke it like this, passing the List.
update.accept(details);
All the above does is encapsulate the for loop (using the enhanced version for simplicity) and perform the operation.
If this is all you wanted, I would recommend just doing it as you were doing it sans the lambda.
I have a list of GroupOffices
List<GroupOffice> officesOfTheGroup;
I want to search whether the id is not equal to groupOffice.getId (Long value) but the label is groupOffice.getLabel (String) and assign it to a boolean. The below code is working good, but is there any better approach instead of go through all items with for loop ?
public GroupOfficeDto saveGroupOffice(GroupOfficeDto groupOfficeDto) {
List<GroupOffice> officesOfTheGroup = //some list from db..
for (GroupOffice go : officesOfTheGroup) {
if ((!go.getId().equals(groupOfficeDto.getId())) && go.getLabel().equals(groupOfficeDto.getLabel())) {
return groupOfficeDto;
}
return null:
}
Or how can I use stream ? If so, is using stream is better approach for it ?
Note: I am using Java8
If you need boolean value you can do:
boolean b = officesOfTheGroup.stream()
.anyMatch(office -> !office.getId().equals(5) && office.getLabel().equals("London"))
is there any better approach instead of go through all items with for loop ?
Notably, your code does not go through all the items if it finds an acceptable one before reaching the end of the list. But if you have only the list to work with, then there is no alternative to being prepared to check every list element. As to whether to use a for loop, I think that's just fine.
Or how can I use stream ? If so, is using stream is better approach for it ?
It is indeed pretty fashionable these days to use streams, and although they seem to be used more than I think they should, yours is not an unreasonable use case. You might write it like this:
public GroupOfficeDto saveGroupOffice(GroupOfficeDto groupOfficeDto) {
List<GroupOffice> officesOfTheGroup = //some list from db..
Integer officeId = groupOfficeDto.getId();
String officeLabel = groupOfficeDto.getLabel();
return officesOfTheGroup.stream()
.filter(o -> !o.getId().equals(officeId))
.anyMatch(o -> o.getLabel().equals(officeLabel))
? groupOfficeDto : null;
}
Particularly relevant here is use of the .anyMatch() terminal operation, because it allows stream processing to finish as soon as the result is determined, like your for loop, rather than processing the entire stream. Also note that the ID and Label against which you are comparing the offices are extracted and stored in variables before the stream expression. This allows them to be "effectively final", as is necessary for them to appear in the lambdas in the stream. It's also slightly more efficient to do it this way, instead of retrieving the same objects from the DTO over and over again -- for the for loop case, too.
Do note that the stream version is not much simpler than the loop version, and not much easier to read, either. I don't personally see much advantage for either one over the other.
Try this:
public GroupOfficeDto saveGroupOffice(GroupOfficeDto groupOfficeDto) {
...
List<GroupOffice> result = officesOfTheGroup.stream()
.filter(it -> it.getLabel().equals(groupOfficeDto.getLabel()))
.filter(it -> !it.getId().equals(groupOfficeDto.getId()))
.collect(Collectors.toList())
...
}
In Java 8,
I currently have a lambda that looks like this:
.createSomething((acceptableStates, someProxy) -> (userId) ->
acceptableStates.contains(someProxy.getSomeAttributeId(userId)))
However, we have changed someProxy.getSomeAttributeId to return an Optional<String> instead of a string.
What's the cleanest / most accepted way to essentially check for if acceptableStates contains the value of someProxy.getSomeAttributeId(userId) if the attribute returned isn't empty?
(Note: acceptableStates is still a list of strings, not of Optional<String>)
.... userId -> someProxy.getSomeAttributeId(userId)
.map(acceptableStates::contains)
.orElse(Boolean.FALSE);
Unfortunately, I see no very clean solution here. Refer to this question: Functional style of Java 8's Optional.ifPresent and if-not-Present?
Additionally, you even have to save the optional in a variable if the call to getSomeAttributeId does cost resources.
.createSomething((acceptableIds, someProxy) -> (userId) ->
{
Optional<String> attrId = someProxy.getSomeAttributeId(userId);
return attrId.isPresent() ? acceptableStates.contains(attrId.get()) : false;
})
I strongly recommend writing understandable and clear code like this snippet:
Optional<SomeAttributeId> optional = someProxy.getSomeAttributeId(userId);
return optional.isPresent() && acceptableStates.contains(optional.get());
If there should not be thrown an exception (1) when someProxy.getSomeAttributeId(userId) is an empty optional:
acceptableStates.contains(someProxy.getSomeAttributeId(userId).orElseThrow(() -> new Exception()))
Or unless you have the default value (2) to fill up the result with:
acceptableStates.contains(someProxy.getSomeAttributeId(userId).orElse(DEFAUT_VALUE))
My point:
Do not pursue Java 8 features which will mess everything up, especially in cases where a simple boolean expression could be used. I had experience refactoring some code toward plain Java statements because new people (1.1) came in a project could not get what the code does. With the time, even I (as a writer) barely can do so (1.2).
Also, with that "lambda chain" style, one slight change could cause rewriting a snippet/method entirely (2).
It is always fun to poke around n-nested lambdas, where n > 2 (3).
Anyway, if you don't share my point, #Eugene proposed a good way to go with.
return value.isPresent() && value.get().contains("some-text");
Is there a way in Java to apply a function to all the elements of a Stream without breaking the Stream chain? I know I can call forEach, but that method returns a void, not a Stream.
There are (at least) 3 ways. For the sake of example code, I've assumed you want to call 2 consumer methods methodA and methodB:
A. Use peek():
list.stream().peek(x -> methodA(x)).forEach(x -> methodB(x));
Although the docs say only use it for "debug", it works (and it's in production right now)
B. Use map() to call methodA, then return the element back to the stream:
list.stream().map(x -> {method1(x); return x;}).forEach(x -> methodB(x));
This is probably the most "acceptable" approach.
C. Do two things in the forEach():
list.stream().forEach(x -> {method1(x); methodB(x);});
This is the least flexible and may not suit your need.
You are looking for the Stream's map() function.
example:
List<String> strings = stream
.map(Object::toString)
.collect(ArrayList::new, ArrayList::add, ArrayList::addAll);
The best option you have is to apply the map to your stream. which returns a stream consisting of the results of applying the given function to the elements of the stream.
For example:
IntStream.range(1, 100)
.boxed()
.map(item->item+3)
.map(item->item*2)...
We are applying several modifications to the stream but in some case we don't want to modify the stream. We just want to visit every element and then pass it down the stream without modification (like the peek() method in the streams API). in such cases, we can
StreamItem peekyMethod(StreamItem streamItemX) {
// .... visit the streamItemX
//Then pass it down the stream
return streamItemX;
}
Not entirely sure what you mean by breaking the stream chain, but any operation on a Stream that returns a Stream will not break or consume your Stream. Streams are consumed by terminal operations and as you noted the forEach does not return a Stream<T> and as such ends the stream, by executing all the intermediate operations before the forEach and the forEach itself.
In the example that you provided in the comments:
myStream.map(obj -> {obj.foo(); return obj;}
You can't really do this with one liner. Of course you could use a method reference, but then your returned Stream would be of a different type (assuming foo returns a type):
myStream.map(Obj::foo) // this will turn into Stream<T>, where T is
// the return type of foo, instead of Stream<Obj>
Besides that your map operation is stateful, which is strongly discouraged. Your code will compile and might even work as you want it to - but it might later fail. map operations should be stateless.
You can use map method but you have to create helper method which returns this. For example:
public class Fluent {
public static <T> Function<T, T> of(Consumer<T> consumer) {
return t -> {
consumer.accept(t);
return t;
};
}
}
And use it when you want to call void method:
list.stream().map(Fluent.of(SomeClass::method));
or if you want to use it with method with some argument:
list.stream().map(Fluent.of(x -> x.method("hello")))
I think you are looking for Stream.peek. But read the docs carefully, as it was designed mainly as a debug method. From the docs:
This method exists mainly to support debugging, where you want to see the elements as they flow past a certain point in a pipeline
The action passed to peek must be non interfering.
I think the cleanest way is to add a mutator to the objects in the stream.
For example,
class Victim {
private String tag;
private Victim withTag(String t)
this.tag = t;
return this;
}
}
List<Victim> base = List.of(new Victim());
Stream<Victim> transformed = base.stream().map(v -> v.withTag("myTag"));
If you prefer (and many will), you can have the withTag method create and return a new Victim; this allows you to make Victim immutable.
Like just about everyone, I'm still learning the intricacies (and loving them) of the new Java 8 Streams API. I have a question concerning usage of streams. I'll provide a simplified example.
Java Streams allows us to take a Collection, and use the stream() method on it to receive a stream of all of its elements. Within it, there are a number of useful methods, such as filter(), map(), and forEach(), which allow us to use lambda operations on the contents.
I have code that looks something like this (simplified):
set.stream().filter(item -> item.qualify())
.map(item -> (Qualifier)item).forEach(item -> item.operate());
set.removeIf(item -> item.qualify());
The idea is to get a mapping of all items in the set, which match a certain qualifier, and then operate through them. After the operation, they serve no further purpose, and should be removed from the original set. The code works well, but I can't shake the feeling that there's an operation in Stream that could do this for me, in a single line.
If it's in the Javadocs, I may be overlooking it.
Does anyone more familiar with the API see something like that?
You can do it like this:
set.removeIf(item -> {
if (!item.qualify())
return false;
item.operate();
return true;
});
If item.operate() always returns true you can do it very succinctly.
set.removeIf(item -> item.qualify() && item.operate());
However, I don't like these approaches as it is not immediately clear what is going on. Personally, I would continue to use a for loop and an Iterator for this.
for (Iterator<Item> i = set.iterator(); i.hasNext();) {
Item item = i.next();
if (item.qualify()) {
item.operate();
i.remove();
}
}
In one line no, but maybe you could make use of the partitioningBy collector:
Map<Boolean, Set<Item>> map =
set.stream()
.collect(partitioningBy(Item::qualify, toSet()));
map.get(true).forEach(i -> ((Qualifier)i).operate());
set = map.get(false);
It might be more efficient as it avoids iterating the set two times, one for filtering the stream and then one for removing corresponding elements.
Otherwise I think your approach is relatively fine.
There are many approaches. If you use myList.remove(element) you must override equals(). What I prefer is:
allList.removeIf(item -> item.getId().equals(elementToDelete.getId()));
Good luck and happy coding :)
After the operation, they serve no further purpose, and should be removed from the original set. The code works well, but I can't shake the feeling that there's an operation in Stream that could do this for me, in a single line.
You cannot remove elements from the source of the stream with the stream. From the Javadoc:
Most stream operations accept parameters that describe user-specified behavior..... To preserve correct behavior, these behavioral parameters:
must be non-interfering (they do not modify the stream source); and
in most cases must be stateless (their result should not depend on any state that might change during execution of the stream pipeline).
What you really want to do is to partition your set. Unfortunately in Java 8 partitioning is only possible via the terminal "collect" method. You end up with something like this:
// test data set
Set<Integer> set = ImmutableSet.of(1, 2, 3, 4, 5);
// predicate separating even and odd numbers
Predicate<Integer> evenNumber = n -> n % 2 == 0;
// initial set partitioned by the predicate
Map<Boolean, List<Integer>> partitioned = set.stream().collect(Collectors.partitioningBy(evenNumber));
// print even numbers
partitioned.get(true).forEach(System.out::println);
// do something else with the rest of the set (odd numbers)
doSomethingElse(partitioned.get(false))
Updated:
Scala version of the code above
val set = Set(1, 2, 3, 4, 5)
val partitioned = set.partition(_ % 2 == 0)
partitioned._1.foreach(println)
doSomethingElse(partitioned._2)`
Nope, your implementation is probably the simplest one. You might do something deeply evil by modifying state in the removeIf predicate, but please don't. On the other hand, it might be reasonable to actually switch to an iterator-based imperative implementation, which might actually be more appropriate and efficient for this use case.
if I understand your question correctly:
set = set.stream().filter(item -> {
if (item.qualify()) {
((Qualifier) item).operate();
return false;
}
return true;
}).collect(Collectors.toSet());
I see Paul's clarity concern when using streams, stated in the top answer. Perhaps adding explaining variable clarifies intentions a little bit.
set.removeIf(item -> {
boolean removeItem=item.qualify();
if (removeItem){
item.operate();
}
return removeItem;
});