I'm working on a problem for school. A lot of this method was already implemented and I'm not allowed to make too many changes.
Actually I can only make changes in specific spots.
Here's the code of the method I'm working on, although some words are in Dutch, it should be readable.
It should read lines of a file, create addresses from the text (which is saved as (street+" "+number+" "+place)) and add them to a list which is returned. The file ends with an empty line.
#Override
public List<Adres> query(ISpecification specification) {
if (specification instanceof FileSpecification) {
if (((FileSpecification) specification).toFileQuery().equals("ALL")) {
ArrayList<Adres> adressen = new ArrayList<>();
/*---start of my code*/
File studentF = new File(fsConnection.getStudentConnection());
try {
FileReader fr = new FileReader(studentF);
BufferedReader br = new BufferedReader(fr);
br.lines().forEach(new Consumer<String>(){
#Override
public void accept(String line) {
String[] words = line.split("\\s");
if(words.length == 3){
/*line i'm having trouble with*/adressen.add(new Adres(words[0], Integer.parseInt(words[1]), words[2]);
}
}
});
} catch (FileNotFoundException ex) {
Logger.getLogger(AdresFile.class.getName()).log(Level.SEVERE, null, ex);//Don't mind this
}
/*---end of my code*/
//System.out.println("query: Nog niet geimplementeerd!");
return adressen;
} else {
return null;
}
} else {
return null;
}
}
As you see I wanted to access a list outside of a consumer block. I know now this is not possible. I thought of creating a different method or so, but that is not allowed. I have to use the foreach method. Any help is appreciated.
When you are working with Java7, then the compiler would need
final ArrayList<Adres> adressen = new ArrayList<>();
there. The point is: you want to use that local variable within an anoymous inner class; in other words: in a context that is somehow decoupled from the class you put the source code. And in order for the decoupled class to able to use adressen needs to be final (so that the compiler knows: that reference will not change later on). And given your comment: no, this doesn't magically turn an object into something that is immutable. It just prevents that the reference changes the "target" it is pointing to!
But as you are not allowed to change that line, you could go for:
ArrayList<Adres> adressen = new ArrayList<>();
final ArrayList<Adres> tempAdressen = adressen;
and then have your code use tempAdressen.
Alternatively, I assume you are using Java7. With Java8, the compiler should be able to understand that adressen is effectively final; and thus it should accept the source code as is.
It seems you are already using Java8, because of the call to lines() in BufferedReader.
So my suggestion is to do a map then a collect to list, instead of a forEach. This way you will not need to access the list from within your Consumer.
adressen.addAll(
br.lines().map(line -> {
String[] words = line.split("\\s");
if (words.length == 3) {
return new Adres(words[0], Integer.parseInt(words[1]), words[2]);
}
return null;
})
.filter(Objects::nonNull)
.collect(Collectors.toList())
);
With Java8, you can use java.util.stream.Collectors to return a list of elements directly from a stream. The use of collectors help you to avoid side effects (in your case, the need to use an external array to put the elements parsed).
I would personally write it using the following lambda:
List<Adres> adressen = br.lines().stream()
.map(line -> line.split("\\s"))
.filter(words -> words.length == 3)
.map(words -> new Adres(words[0], Integer.parseInt(words[1]), words[2]))
.collect(Collectors.toList());
This will work, yet it won't handle the case in which the data is malformed. To solve that problem, one can change the code above to handle if a line is not composed of 3 elements by modifying the lambda (even if this is not very elegant):
List<Adres> adressen = br.lines().stream()
.map(line -> line.split("\\s"))
.filter(words -> {
if (words.length == 3)
return true;
else {
throw new IllegalArgumentException();
}
})
.map(words -> new Adres(words[0], Integer.parseInt(words[1]), words[2]))
.collect(Collectors.toList());
Related
I couldn't wrap my head around writing the below condition using Java Streams. Let's assume that I have a list of elements from the periodic table. I've to write a method that returns a String by checking whether the list has Silicon or Radium or Both. If it has only Silicon, method has to return Silicon. If it has only Radium, method has to return Radium. If it has both, method has to return Both. If none of them are available, method returns "" (default value).
Currently, the code that I've written is below.
String resolve(List<Element> elements) {
AtomicReference<String> value = new AtomicReference<>("");
elements.stream()
.map(Element::getName)
.forEach(name -> {
if (name.equalsIgnoreCase("RADIUM")) {
if (value.get().equals("")) {
value.set("RADIUM");
} else {
value.set("BOTH");
}
} else if (name.equalsIgnoreCase("SILICON")) {
if (value.get().equals("")) {
value.set("SILICON");
} else {
value.set("BOTH");
}
}
});
return value.get();
}
I understand the code looks messier and looks more imperative than functional. But I don't know how to write it in a better manner using streams. I've also considered the possibility of going through the list couple of times to filter elements Silicon and Radium and finalizing based on that. But it doesn't seem efficient going through a list twice.
NOTE : I also understand that this could be written in an imperative manner rather than complicating with streams and atomic variables. I just want to know how to write the same logic using streams.
Please share your suggestions on better ways to achieve the same goal using Java Streams.
It could be done with Stream IPA in a single statement and without multiline lambdas, nested conditions and impure function that changes the state outside the lambda.
My approach is to introduce an enum which elements correspond to all possible outcomes with its constants EMPTY, SILICON, RADIUM, BOTH.
All the return values apart from empty string can be obtained by invoking the method name() derived from the java.lang.Enum. And only to caver the case with empty string, I've added getName() method.
Note that since Java 16 enums can be declared locally inside a method.
The logic of the stream pipeline is the following:
stream elements turns into a stream of string;
gets filtered and transformed into a stream of enum constants;
reduction is done on the enum members;
optional of enum turs into an optional of string.
Implementation can look like this:
public static String resolve(List<Element> elements) {
return elements.stream()
.map(Element::getName)
.map(String::toUpperCase)
.filter(str -> str.equals("SILICON") || str.equals("RADIUM"))
.map(Elements::valueOf)
.reduce((result, next) -> result == Elements.BOTH || result != next ? Elements.BOTH : next)
.map(Elements::getName)
.orElse("");
}
enum
enum Elements {EMPTY, SILICON, RADIUM, BOTH;
String getName() {
return this == EMPTY ? "" : name(); // note name() declared in the java.lang.Enum as final and can't be overridden
}
}
main
public static void main(String[] args) {
System.out.println(resolve(List.of(new Element("Silicon"), new Element("Lithium"))));
System.out.println(resolve(List.of(new Element("Silicon"), new Element("Radium"))));
System.out.println(resolve(List.of(new Element("Ferrum"), new Element("Oxygen"), new Element("Aurum")))
.isEmpty() + " - no target elements"); // output is an empty string
}
output
SILICON
BOTH
true - no target elements
Note:
Although with streams you can produce the result in O(n) time iterative approach might be better for this task. Think about it this way: if you have a list of 10.000 elements in the list and it starts with "SILICON" and "RADIUM". You could easily break the loop and return "BOTH".
Stateful operations in the streams has to be avoided according to the documentation, also to understand why javadoc warns against stateful streams you might take a look at this question. If you want to play around with AtomicReference it's totally fine, just keep in mind that this approach is not considered to be good practice.
I guess if I had implemented such a method with streams, the overall logic would be the same as above, but without utilizing an enum. Since only a single object is needed it's a reduction, so I'll apply reduce() on a stream of strings, extract the reduction logic with all the conditions to a separate method. Normally, lambdas have to be well-readable one-liners.
Collect the strings to a unique set. Then check containment in constant time.
Set<String> names = elements.stream().map(Element::getName).map(String::toLowerCase).collect(toSet());
boolean hasSilicon = names.contains("silicon");
boolean hasRadium = names.contains("radium");
String result = "";
if (hasSilicon && hasRadium) {
result = "BOTH";
} else if (hasSilicon) {
result = "SILICON";
} else if (hasRadium) {
result = "RADIUM";
}
return result;
i have used predicate in filter to for radium and silicon and using the resulted set i am printing the result.
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
public class Test {
public static void main(String[] args) {
List<Element> elementss = new ArrayList<>();
Set<String> stringSet = elementss.stream().map(e -> e.getName())
.filter(string -> (string.equals("Radium") || string.equals("Silicon")))
.collect(Collectors.toSet());
if(stringSet.size()==2){
System.out.println("both");
}else if(stringSet.size()==1){
System.out.println(stringSet);
}else{
System.out.println(" ");
}
}
}
You could save a few lines if you use regex, but I doubt if it is better than the other answers:
String resolve(List<Element> elements) {
String result = elements.stream()
.map(Element::getName)
.map(String::toUpperCase)
.filter(str -> str.matches("RADIUM|SILICON"))
.sorted()
.collect(Collectors.joining());
return result.matches("RADIUMSILICON") ? "BOTH" : result;
}
I have one code snippet, which is calling 2 different services based on some a if condition. And both the services return CompletableFuture<Optional<SomeObject>>. Following is the code logic looks like
if(someCondition){
CompletableFuture<Optional<SomeObjectType1>> = service1.call();
}else{
CompletableFuture<Optional<SomeObjectType2>> = service2.call();
}
And both SomeObjectType1 and SomeObjectType2 have a String inside it, which is of my interest. My current code looks like this:
private ContentWrapper getContentWrapper(input1, input2, ....) {
String content = null;
if (some_condition is true) {
List<Object_Type_1> list = service1.fetchTheCompletableFuture(..... inputs...)
.join()
.map(ListOutput::getList)
.orElse(null);
if (CollectionUtils.isNotEmpty(list)) {
content = list.get(0).getContent();
}
} else {
content = service2
.fetchTheCompletableFuture(..... inputs...)
.join()
.map(RenderedContent::getContent)
.orElse(null);
}
return content != null ? new ContentWrapper(content) : null;
}
Now my question is, can this if-else clause be removed or make it more clear by using lambdas. I am new in lambdas and does not have very good idea on this.
I am not sure whether the code below even compiles due to the vagueness.
private ContentWrapper getContentWrapper(input1, input2, ....) {
Optional<RenderedContent> content = some_condition
? service1
.fetchTheCompletableFuture(..... inputs...)
.join()
.map(ListOutput::getList)
.stream()
.findFirst()
: service2
.fetchTheCompletableFuture(..... inputs...)
.join();
}
return content
.map(RenderedContent::getContent)
.map(ContentWrapper::new).orElse(null);
}
The first service seems to yield a list of RenderedContent of which to take the first if there is one.
The second service may yield a Rendered content immediately.
So you can join the if-else to an Optional<RenderedContent>.
The map(RenderedContent::getContent) will yield Optional.empty() if it was empty to begin with. Otherwise getContent is called and wrapped in an Optional.
If present new ContentWrapper(...) might be called.
Notice much may fail, like getContent returning null (though there is an Optional.ofNullable.
Nevertheless Streams may be very expressive.
I would avoid using null in favor of Optional as that plays better together.
I keep getting told it is bad practice to not terminate a Stream via methods such as collect and findFirst but no real feedback as to why not much said about it in blogs.
Looking at following example, instead of using a massive nested if check, I went with Optional to get back a List value. As you can see my last step is filter in that Stream. This works as expected for me which is to get back a list. Why is this wrong and how should I have written it instead?
import lombok.Getter;
import lombok.Setter;
import java.util.*;
public class Main {
public static void main(String[] args) {
RequestBean requestBean = new RequestBean();
// if I uncomment this I will get the list values printed as expected
// FruitBean fruitBean = new FruitBean();
// AnotherBean anotherBean = new AnotherBean();
// InnerBean innerBean = new InnerBean();
// requestBean.setFruitBeans(Collections.singletonList(fruitBean));
// fruitBean.setAnotherBeans(Collections.singletonList(anotherBean));
// anotherBean.setInnerBeans(Collections.singletonList(innerBean));
// List<String> beans = Arrays.asList("apple", "orange");
// innerBean.setBeans(beans);
List<String> result = getBeanViaOptional(requestBean);
if(result != null){
for(String s : result){
System.out.println(s);
}
}else {
System.out.println("nothing in list");
}
}
private static List<String> getBeanViaOptional(RequestBean bean){
Optional<List<String>> output = Optional.ofNullable(bean)
.map(RequestBean::getFruitBeans)
.map(n -> n.get(0))
.map(FruitBean::getAnotherBeans)
.map(n -> n.get(0))
.map(AnotherBean::getInnerBeans)
.map(n -> n.get(0))
.map(InnerBean::getBeans)
// why is this bad practice to end with a filter. how should I write this then?
.filter(n -> n.contains("apple"));
if(!output.isPresent()){
throw new CustomException();
}
return output.get();
}
// not using this. just to show that optional was preferable compared to this.
private static List<String> getBeanViaIfChecks(RequestBean bean){
if(bean != null){
if(bean.getFruitBeans() != null){
if(bean.getFruitBeans().get(0) != null){
if(bean.getFruitBeans().get(0).getAnotherBeans() != null){
if(bean.getFruitBeans().get(0).getAnotherBeans().get(0) != null){
if(bean.getFruitBeans().get(0).getAnotherBeans().get(0).getInnerBeans() != null){
if(bean.getFruitBeans().get(0).getAnotherBeans().get(0).getInnerBeans().get(0) != null){
return bean.getFruitBeans().get(0).getAnotherBeans().get(0).getInnerBeans().get(0).getBeans();
}
}
}
}
}
}
}
return null;
}
}
#Getter
#Setter
class RequestBean{
List<FruitBean> fruitBeans;
}
#Getter
#Setter
class FruitBean{
List<AnotherBean> anotherBeans;
}
#Getter
#Setter
class AnotherBean{
List<InnerBean> innerBeans;
}
#Getter
#Setter
class InnerBean{
List<String> beans;
}
class CustomException extends RuntimeException{
// do some custom exception stuff
}
I keep getting told it is bad practice to not terminate a Stream via
methods such as collect and findFirst but no real feedback as to why
not much said about it in blogs.
It really depends on the context, if you're saying "can I end a stream with an intermediate operation e.g. filter and not call a terminal operation (an operation that consumes the stream) ever" then yes it's bad practise and kind of pointless because you've just defined some criteria but never asked for the "result".
Streams are lazy in the sense that they don't do anything unless told so by a terminal operation e.g. collect, findFirst etc.
If you're saying "is it bad practice to return a stream from a method" then
it may be worth reading this answer on whether one should return a stream or a collection.
Further, note that your getBeanViaOptional logic is operating on an Optional<T> rather than a Stream<T>. Yes, they both have map, flatMap and filter but note that an Optional<T> can only contain one value or it's empty whereas a stream can have one or more.
Your approach of using an Optional instead of the imperative ifs is obviously better in terms of readability, maintenance, etc. so I'd suggest you proceed with that approach, although you can improve it a little bit by using orElseThrow i.e.:
return Optional.ofNullable(bean)
.map(RequestBean::getFruitBeans)
.map(n -> n.get(0))
.map(FruitBean::getAnotherBeans)
.map(n -> n.get(0))
.map(AnotherBean::getInnerBeans)
.map(n -> n.get(0))
.map(InnerBean::getBeans)
.filter(n -> n.contains("apple"))
.orElseThrow(CustomException::new);
With streams usually none of the intermediate operations will be executed when there is no terminal operation. Your example uses Optional. Its operations map and filter have the same name as some intermediate operations in stream, but they are different. Your example is ok (not bad practice) at the line asked by your question.
Another thing is that (as already pointed out by Aomine) .orElseThrow is the shorter way to get the value in the Optional and throw an exception if there is none. Even more important is that it is safer to use .orElseThrow
(or .orElse if there is a default value). Optional.get() should be avoided whenever possible. You will get a NoSuchElementException if there is no value. This is almost as bad as getting a NullPointerException when not using Optional. Optional used in a proper way can protect you from NullPointerException.
I have a POJO:
class MyObject {
private Double a;
private String b;
//constructor, getter + setter
}
Some function is creating a list of this POJO. Some values for a might be null, so I want to replace them with 0.0. At the moment I am doing it like this.
public List<MyObject> fetchMyObjects(Predicate predicate) {
List<MyObject> list = getMyListsOfTheDatabase(predicate);
list
.forEach(myObject -> {
if (myObject.getA() == null) {
myObject.setA(0.0);
}
});
return list;
}
Is there a way to integrate the forEach in the return? Something like
return list
.stream()
.someStatement();
It's not about, if this is the best place to convert the nulls to zero, but rather a questions to better understand the streaming api.
Use the peek function
Returns a stream consisting of the elements of this stream, additionally performing the provided action on each element as elements are consumed from the resulting stream.
public List<MyObject> fetchMyObjects(Predicate predicate) {
return getMyListsOfTheDatabase(predicate)
.stream()
.peek(it -> if(it.getA() == null) it.setA(0.0))
.collect(Collectors.toList());
}
While others have been happy to answer your question as it stands, allow me to step a step back and give you the answer you didn’t ask for (but maybe the answer that you want): You don’t want to do that. A stream operation should be free from side effects. What you are asking for is exactly a stream operation that has the side effect of modifying the original objects going into the stream. Such is poor code style and likely to confuse those reading your code after you.
The code you already have solves your problem much more nicely than any combined stream pipeline.
What you may want to have if you can modify your POJO is either a constructor that sets a to 0 if null was retrieved from the database, or method that does it that you may call from list.forEach:
list.forEach(MyObject::setAToZeroIfNull);
It's not about, if this is the best place to convert the nulls to
zero, but rather a questions to better understand the streaming api.
That’s fair. In any case I will let this answer stand for anyone else popping by.
You can't return the same List instance with a single statement, but you can return a new List instance containing the same (possibly modified) elements:
return list.stream()
.map(myObject -> {
if (myObject.getA() == null) {
myObject.setA(0.0);
}
return myObject;
})
.collect(Collectors.toList());
Actually you should be using List::replaceAll:
list.replaceAll(x -> {
if(x.getA() == null) x.setA(0.0D);
return x;
})
forEach doesn't have a return value, so what you might be looking for is map
return list
.stream()
.map(e -> {
if (e.getA() == null) e.setA(0d);
return e;
})
.whateverElse()...
The following would be fine:
list.stream()
.filter(obj -> obj.getA() == null)
.forEach(obj -> obj.setA(0.0));
return list;
However in your case just returning a Stream might be more appropriate (depends):
public Stream<MyObject> fetchMyObjects(Predicate predicate) {
return getMyListsOfTheDatabase(predicate);
}
public Stream<MyObject> streamMyObjects(List<MyObject> list) {
return list.stream()
.peek(obj -> {
if (obj.getA() == null) {
obj.setA(0.0);
}
});
}
I personally never used peek, but here it corrects values.
On code conventions, which are more string in the java community:
Indentation: Java took 4 as opposed to C++'s 3 as more separate methods,
and less indentation was expected. Debatable but okay.
For generic type parameters often a single capital like T, C, S.
For lambda parameters short names, often a single letter, hence I used obj.
I was refactoring some old code of mine that I've written and I stumbeled on this code:
List<OcmImageData> fullImagePool = new ArrayList<>();
if (CollectionUtils.isNotEmpty(style.getTestMH())) {
fullImagePool.addAll(style.getTestMH());
}
if (CollectionUtils.isNotEmpty(style.getTrousers())) {
fullImagePool.addAll(style.getTrousers());
}
if (CollectionUtils.isNotEmpty(style.getDetailRevers())) {
fullImagePool.addAll(style.getDetailRevers());
}
if (CollectionUtils.isNotEmpty(style.getDetailCuffs())) {
fullImagePool.addAll(style.getDetailCuffs());
}
if (CollectionUtils.isNotEmpty(style.getDetailInner())) {
fullImagePool.addAll(style.getDetailInner());
}
if (CollectionUtils.isNotEmpty(style.getDetailMaterial())) {
fullImagePool.addAll(style.getDetailMaterial());
}
if (CollectionUtils.isNotEmpty(style.getComposing())) {
fullImagePool.addAll(style.getComposing());
}
...
So basically I need to create an ArrayList which contains all Lists here referenced, because those can be null (they are fetched out of the database from an closed sourced framework, and unfortunately its null if he doesn't find anything), I need to check everytime if the collection is not null to add them into this pool which looks just weird.
Is there a library or Collection-Framework utility class that gives me the posibility to add a collection to another without performing the null-safe check?
In Java 8 Use below code:-
Optional.ofNullable(listToBeAdded).ifPresent(listToBeAddedTo::addAll)
listToBeAdded - The list whose elements are to be added.
listToBeAddedTo - The list to which you are adding elements using addAll.
Just write a small utility method:
public static <E> void addAllIfNotNull(List<E> list, Collection<? extends E> c) {
if (c != null) {
list.addAll(c);
}
}
so that you can write:
List<OcmImageData> fullImagePool = new ArrayList<>();
addAllIfNotNull(fullImagePool, style.getTestMH());
addAllIfNotNull(fullImagePool, style.getTrousers());
addAllIfNotNull(fullImagePool, style.getDetailRevers());
// ...etc
Using Java 8:
List<OcmImageData> fullImagePool = Stream.of(style.getTestMH(), /* etc */)
.filter(Objects::nonNull)
.flatMap(l -> l.stream())
.collect(Collectors.toList());
This refactors cleanly to
for (OcmImageData elem : new List<OcmImageData>[] { style.getTestMH(), style.getTrousers() /* etc */}) {
if (CollectionUtils.isNotEmpty(elem)) {
fullImagePull.addAll(elem);
}
}
To answer your original question, no, you will have to do your own null check. You can see Guava's methods will throw an NPE, and Apache's methods explicitly require the input to be not null.