0

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())));
}
Jim C
  • 3,957
  • 25
  • 85
  • 162

1 Answers1

1

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.

Steffen Harbich
  • 2,639
  • 2
  • 37
  • 71
  • can you add your veiw about these 3 remaing points: 1. What approach and which part you have in mind when you wrote "I wouldn't use map stream method when you really do not map elements of the stream"? I guess you are talking about ".map(x -> System.out.println...", right? 2. Regard forEach, if you gave me a tip I didn't get it. Do you see any odd use of forEach in aproach 2 or 3? 3. If onderneming.getAdressen() can return NoSuchElementException in the first approach, it is almost the same to say that I didn't get any advantage to use Optional.ofNullable(onderneming.getAdressen()) – Jim C Nov 24 '17 at 13:42
  • 1
    1.) yes `map(x -> System.out.println..` would map objects of type `AdresOndernemingType` to `Void` but this doesn't make much sense because you do not do anything with the resulting `Void` objects (and you cannot do anything with them). That `println` is an action you want to do for each element of the stream, so `forEach` is your friend. You can read [here](https://www.mkyong.com/java8/java-8-streams-map-examples/) about map examples. – Steffen Harbich Nov 26 '17 at 15:50
  • 1
    2.) no no, your `forEach` call in approach 3 is just fine. But I don't see it in approach 2 :) – Steffen Harbich Nov 26 '17 at 15:53
  • 1
    3.) true, I wouldn't use `Optional` there. As far as I know, it is intended to be used as return value of methods to indicate that the result can be "empty". – Steffen Harbich Nov 26 '17 at 15:59