7

I have a class where optionally a Comparator can be specified.


Since the Comparator is optional, I have to evaluate its presence and execute the same stream code, either with sorted() or without:

if(comparator != null) { [...].stream().map()[...].sorted(comparator)[...]; } else { [...].stream().map()[...]; } 

Question:
Is there a more elegant way to do this without the code duplication?

Note:
A default Comparator is not an option, I just want to keep the original order of the values I am streaming.

Besides, the elements are already mapped at the point of sorting, so I can not somehow reference the root list of the stream, as I do not have the original elements anymore.

4
  • Select [...].stream().map()[...] and choose your IDE "extract variable" refactoring maybe? Commented Jul 27, 2017 at 6:09
  • [...].stream().map()[...].sorted((a,b) -> comparator == null ? 0 : comparator.compare(a,b))[...]; Commented Jul 27, 2017 at 6:15
  • 2
    You can put the stream instance in a variable ( Stream class) and then if a comparator is present, call the sort method of that class? Then collect the result.... Commented Jul 27, 2017 at 6:15
  • 3
    @Lino: more efficient would be .sorted(comparator == null? (a,b) -> 0: comparator), not repeating the null-check in every comparison. Still, it implies an unnecessary sorting process when comparator is null. Commented Jul 27, 2017 at 12:04

6 Answers 6

9

You can do something like this:

Stream<Something> stream = [...].stream().map()[...]; // preliminary processing if(comparator != null) { stream = stream.sorted(comparator); // optional sorting } stream... // resumed processing, which ends in some terminal operation (such as collect) 
Sign up to request clarification or add additional context in comments.

6 Comments

How does this make the above code better? In fact, if you are using Streams it's always a good practice to chain in one statement.
@AshishLohia Uhmm... allow me to disagree with you. This approach is readable and correct.
@AshishLohia why is it always better to do it in one statement? This would lead to duplication of the map, which is worse.
@Ashish Lohia: it’s a good practice to chain in one statement, but that doesn’t imply that it is always the best practice. You have to trade off between that good practice and the other food practice, avoiding code duplication…
I have to honestly admit that I often forget (the question is a proof) that a stream can be assigned to a variable as it is just a normal class instance. It's somehow this thinking of chaining the stream operations that let's you forget this from my pov.
|
4

Another way would be to use Optional:

Stream<Whatever> stream = [...].stream().map()[...]; List<WhateverElse> result = Optional.ofNullable(comparator) .map(stream::sorted) .orElse(stream) .[...] // <-- go on with the stream pipeline .collect(Collectors.toList()); 

2 Comments

Also a great idea to keep the functional syntax.
@JDC Thank you! Yes, that was my idea, however you shouldn't use Optional this way all the time. Stuart Marks and Brian Goetz (parents of this Optional creature) recommend to not use Optional only for method chaining. Instead, they say that you should use it ideally as the return value of methods, where the returned value might be null. I think that this fits your use case, because you might have a method that returns an Optional wrapping your nullable comparator.
3

You could define a comparator of your type (I used E as a placeholder here) that will not change the order:

 Comparator<E> NO_SORTING = (one, other) -> 0; 

If the comparator field is an Optional of Comparator, you can then use

.sorted(comparator.orElse(NO_SORTING)) 

5 Comments

Returning 0 means the objects are equal. Is it guaranteed that the stream sorting does not shift around equal objects?
I would not expect it to, but you would have to consult the documentation, @JDC. Good question.
The documentation of .sorted() states it is at least stable for ordered streams. The order of equal elements should be kept then. So the mentioned answer is indeed correct for ordered streams.
It's correct, but it could come at a significant cost. Sorting is a relatively expensive operation and it's entirely unnecessary here.
@shmosel Thats true. Personally I'd never sort a stream anyway.
2

If you don't mind use third party library StreamEx

StreamEx(source).[...].chain(s -> comparator == null ? s : s.sorted(comparator)).[...]; 

Comments

0

You can accomplish this using an auxiliary function.

static <T, R> R applyFunction(T obj, Function<T, R> f) { return f.apply(obj); } 

and

applyFunction([...].stream().map()[...], stream -> comparator == null ? stream : stream.sorted(comparator)) [...]; 

You don't need to know intermediate stream type.

Comments

0

Java has nothing built-in for this case yet, and passing "null" would lead to an exception.

You can create a util class StreamUtils and use this function

 /** * Resets a .stream().sorted() call and can be used for optional sorters * * Optional * .ofNullable(sorter) * .orElse(StreamUtils.naturalOrder()) */ public static <T> Comparator<T> naturalOrder() { return (o1, o2) -> 0; } 

1 Comment

"naturalOrder" already has a meaning in Java, which is the ordering of the elements according to Comparable.compareTo. It's used in this manner in e.g. Comparable.naturalOrder. unordered or allEqual or something to that effect would be less confusing.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.