I consider a very easy task parse a nested object asnwered by a soap webservice using Java 8 streams. Nevertheless, I am quite confused when I think about the correct or most appropriate approach to use. I know it will depend on circunstances and there will never be a simple recipe. I have been reading for the last two weeks where and how to use stream but I couldn't reach a final conclusion about few options. I put bellow four approaches I would appreciatte if someone could give technical opnion if I understood correctly the real application based on very common requirements when dealing with soap client.
I am not loking for a simple answer like "Here I do successfully this way so you can copy and paste similar idea". I am really interested to understand if I am applying properly what I have read so far.
Firstly, my nested objects answered by web service:
//first level
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "OndernemingAlgemeenType", propOrder = {
"adressen",
... others properties
})
@XmlSeeAlso({
Onderneming20Type.class
})
public class OndernemingAlgemeenType
{
@XmlElement(name = "Adressen")
protected AdresOndernemingLijstType adressen;
... others elements
}
//second level that returns a list
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "AdresOndernemingLijstType", propOrder = {
"adres"
})
public class AdresOndernemingLijstType {
@XmlElement(name = "Adres", required = true)
protected List<AdresOndernemingType> adres;
...
}
// third level used to filter the list and return just one AdresOndernemingType
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "AdresOndernemingType", propOrder = {
"type"
})
public class AdresOndernemingType
extends AdresOndernemingBasisType{
@XmlElement(name = "Type", required = true)
protected TypeAdresOndernemingType type;
}
// fourth level
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "AdresOndernemingBasisType", propOrder = {
... not relevant for this question
})
@XmlSeeAlso({
AdresOndernemingType.class
})
public class AdresOndernemingBasisType
extends AdresBasisType
{
... not relevant for this question
}
// fifth level and finally, the desired fields (street and city)
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "AdresBasisType", propOrder = {
"straat", //street
"gemeente" //city
})
@XmlSeeAlso({
AdresOndernemingDeelnemingInType.class,
AdresOndernemingBasisType.class
})
public class AdresBasisType {
@XmlElement(name = "Straat")
protected StraatRR20Type straat;
@XmlElement(name = "Gemeente")
protected GemeenteOptioneel20Type gemeente;
// Approaches with its understanding
Approach 1: I understand that this is null exception safe. I mean, in case either getAdressen or getAdres is null there will be no exception at all and no address printed.
private void printOptionalDidactic(){
Optional<AdresOndernemingLijstType> op = Optional.ofNullable(onderneming.getAdressen());
op.map(AdresOndernemingLijstType::getAdres).get().stream().filter(Objects::nonNull)
.filter(o -> "001".equals(o.getType().getCode().getValue().getValue()))
.map(x -> System.out.println("My street is: ".concat(x.getStraat())));
}
Approach 2: Assuming that there will be never a null element (minOccurs="1" in xsd for all) so using Optional would be pointless
private void printNoOptionalDidactic(){
onderneming.getAdressen().getAdres().stream()
.filter(o -> "001".equals(o.getType().getCode().getValue().getValue()))
.map(x -> System.out.println("My street is: ".concat(x.getStraat())));
}
Approach 3: Assuming I want to print all streets and I don't care about filtering, I understand there is no advantage at all to use flatMap before forEach Replace nested loop with Java 8 flatmap
private void printForEachDidactic(){
onderneming.getAdressen().getAdres().stream()
.forEach(x -> System.out.println("My street is: ".concat(x.getStraat())));
}
Approach 4 Since no shared resource is used by the predicates and functions used in the process, I understand I could use parallelism. Nevertheless, provided that it is little data so I have no real gain on it Should I always use a parallel stream when possible?
private void printParallelDidactic(){
onderneming.getAdressen().getAdres().parallelStream()
.filter(o -> "001".equals(o.getType().getCode().getValue().getValue()))
.map(x -> System.out.println("My street is: ".concat(x.getStraat())));
}
I wouldn't use map
stream method when you really do not map elements of the stream (e.g. in approach 1 .map(x -> System.out.println...
). Better call forEach
, it is dedicated to execute code for each element of the stream.
Regarding your first approach, I think there will be a NoSuchElementException
thrown if onderneming.getAdressen()
is null. See implementation of Optional.get
:
public T get() {
if (value == null) {
throw new NoSuchElementException("No value present");
}
return value;
}
Otherwise all approaches look valid from the code point of view. Of course I cannot say anything about your data model.
However, you do not need to apply Java 8 streams and lambdas just because Java 8 introduced them. For example, I wouldn't use streams in your approach 3. An old school for loop will do it, too (and is slightly more readable here IMO):
for (AdresOndernemingType x : onderneming.getAdressen().getAdres()) {
System.out.println("My street is: ".concat(x.getStraat())));
}
Regarding parallel streams, I wouldn't use parallel()
except when I really need parallelism to gain performance on huge data sets or long running tasks.