3

I have a List of objects each of which has a method getNumOrder that returns a string. The objects in the list are sorted in lexicographical order. Most of the times all the objects in the list have getNumOrder that return an integer (but not always).

If all the elements in the lists have NumOrders that are integers I would like to sort them using integer sort order. Otherwise they will remain sorted lexicographically.

I have tried this using Java 8 Lambdas

try {
    results.sort((r1,r2)-> {
        Integer seq1= new Integer(r1.getNumOrder());
        Integer seq2= new Integer(r2.getNumOrder());
        seq1.compareTo(seq2);
    });
}
catch (Exception e) {
    // Do nothing: Defaults to lexicographical order.
}

There are 2 issues:

  1. Because I dont know Lambda expressions very well the syntax is incorrect

  2. I am not sure if this will work if the sorting by integer order fails, i.e. whether it will leave the list sorted lexicographically.

4
  • 1
    you have to return "seq1.compareTo(seq2);" Commented Mar 19, 2018 at 15:38
  • 1
    Use Comparator.comparingInt(r -> Integer.parseInt(r.getNumOrder())). Commented Mar 19, 2018 at 17:29
  • @shmosel does that give an advantage over the .sort()-Method? Commented Mar 19, 2018 at 17:35
  • @watchme You would pass that to the sort() method. It's basically a shorter equivalent to Andreas' answer. Commented Mar 19, 2018 at 17:39

3 Answers 3

4

Do not use new Integer(String) to parse a number. It has been deprecated in Java 9. Use Integer.parseInt​(String s) instead, or Integer.valueOf​(String s) if you insists on boxing the value (a waste in this case).

The lambda you're after would be:

results.sort((r1,r2) -> Integer.compare(Integer.parseInt(r1.getNumOrder()),
                                        Integer.parseInt(r2.getNumOrder())));

This leaves results unmodified if the sort fails, because the sort() method copies the list elements to an array, sorts the array, then copies the elements back.

If you prefer to store intermediate values in variables, e.g. so you can see them when debugging, you will be using a lambda block and it needs a return statement. That's what is missing in your code.

results.sort((r1,r2) -> {
        int i1 = Integer.parseInt(r1.getNumOrder());
        int i2 = Integer.parseInt(r2.getNumOrder());
        return Integer.compare(i1, i2);
});

The below part of the answer was based on misunderstanding that input was list of strings, which it's not, so it doesn't apply to question, but I'm leaving it for others who might have similar problems where input is a list of strings.

But it's not good performance. If strings don't have leading zeroes that need to be retained, it'd be better to parse to int, sort the int values, then format back to string.

Using streams (Java 8+):

results = results.stream()
                 .mapToInt(Integer::parseInt)
                 .sorted()
                 .mapToObj(Integer::toString)
                 .collect(Collectors.toList());

Or this non-stream version (Java 1.2+):

int[] arr = new int[results.size()];
int i = 0;
for (String s : results)
    arr[i++] = Integer.parseInt(s);
Arrays.sort(arr);
results.clear();
for (int v : arr)
    results.add(Integer.toString(v));
Sign up to request clarification or add additional context in comments.

15 Comments

@Andreas +1 if you include the solution to his problem (the missing return statement) in order to include the information the OP wanted to know and those improvements.
@watchme OP is not asking for a solution to what OP tried, but for a solution to how to sort, and I gave 3 solutions that are all better than what OP tried to do. I see no reason to fix OPs attempt, which uses deprecated methods.
I think its good to know whats wrong with his method (the missing return statement), especially in the beginning of learning lambdas.
@Mikejg101 You missed the point. I didn't say "don't go there" because of performance, I said it because the code was using deprecated methods. "Deprecated" means "don't use for new code", and this is obviously "new code".
@Andreas fair enough. I think you have an almost complete answer. At the moment, this is more of a comment. My point was that you must answer the question first and then offer helpful hints. Just like everything else, you pseudo answer will become dated eventually as well. In the end, the answer that shows how to return is significantly more important than details on deprecation. Especially since it answers the specific question the OP asked.
|
2

In order to make your code work, just return "seq1.compareTo(seq2);" like this:

try {
      results.sort((r1,r2)-> {
                        Integer seq1= new Integer(r1.getNumOrder());
                        Integer seq2= new Integer(r2.getNumOrder());
                        return seq1.compareTo(seq2);
                });
}
catch (Exception e) {
    // Do nothing: Defaults to lexicographical order.
}

As @Andreas pointed out in the comments, it shouldn't be done that way (with boxing and with using the constructor to parse a String to an integer), there is a cleaner and "not so wastful" way to accomplish that. Here's the code:

try {
       results.sort((r1,r2)-> 
                     Integer.compare(Integer.parseInt(r1.getNumOrder()),
                                     Integer.parseInt(r2.getNumOrder()))
                );
}
catch (Exception e) {
    // Do nothing: Defaults to lexicographical order.
}

Remember here that you only need to (explicitly) return a value in lambdas when you use brackets, as its then handled like a method which - in the case of "sort" - should return an Integer.

5 Comments

Boxing the integers is a waste.
If the list has 10 elements, one or more of which dont return an integer order will the sort fail globally and leave the original order unchanged or will it leave a partially ordered list?
@JennyToy If the sort fails, the list is left unchanged. This is because the sort() method copies the list elements to an array, sorts the array, then copies the elements back.
@Andreas indeed you're right. Nevertheless I thought its important to tell the OP whats wrong with his method of accomplishing that.
Excellently answered the question the OP asked.
1

You just forgot a "return" in your lambda :

return seq1.compareTo(seq2);

Other than that, seems fine, even if it fails

2 Comments

WHat is the difference between this answer and the answer of "watchme"?
approx 2 seconds in posting time I guess

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.