-1

I have a simple List which is ordered.

A = ["a","b","c","d","e"];

I need to generate a subset of the list based on the criteria. If an element E exists in the array, then return all the elements in the List after the element E, otherwise return null.

Normally, I'd do this something like:

int index = A.indexOf("e");
List<String> subList = A.subList(index, A.size());

Can this be done with an equivalent lambda expression?

chrisrhyno2003
  • 3,906
  • 8
  • 53
  • 102
  • 2
    What does the list being ordered have to do with anything? Then, as far as your code goes, don't you need to include a check that `index >= 0`? Also, you need to supply a variable name for the sublist. Finally, the second argument to `subList` is exclusive, so you should pass `A.size()` instead of `A.size() - 1` to avoid chopping off the last element. – Ted Hopp Oct 07 '16 at 20:13
  • I think the question has its merits. It gave me the opportunity to play with a stateful Predicate, which I hadn't done in the past. – GuiSim Oct 07 '16 at 20:41
  • The thing, you’d do normally, still is the best solution. Well, if the list is sorted, you may use `int index = Collections.binarySearch(A, "e");` instead. Then proceed with `List subList = index<0? null: A.subList(index, A.size());`. Stream do neither, binary search nor `subList`, collecting into a new `List` is not the same as getting a `subList`. – Holger Oct 10 '16 at 17:15

2 Answers2

0

If the elements of the list are Comparable, then you could do it like this:

List<String> subList = list.stream()
    .filter(elem -> elem.compareTo("e") > 0)
    .collect(Collectors.toList());

This returns an empty list.

Slightly changing the filter condition:

List<String> subList = list.stream()
    .filter(elem -> elem.compareTo("e") >= 0)
    .collect(Collectors.toList());

Returns a list that only contains "e".

Note that you don't need the list to be ordered for this to work. Besides, you don't even need the element to be present in the list, i.e. if the list was as follows:

List<String> list = Arrays.asList("a", "b", "d", "e");

And you modify the filter condition to return elements after "c":

List<String> subList = list.stream()
    .filter(elem -> elem.compareTo("c") > 0)
    .collect(Collectors.toList());

The result would be ["d", "e"].

fps
  • 33,623
  • 8
  • 55
  • 110
-2

If you really want to use Java 8 Streams and Lambads, you could use a stateful Predicate

public class IsAfterString implements Predicate<String> {

    private final String stringToWatchFor;
    private boolean foundString = false;

    public IsAfterString(String stringToWatchFor) {
      this.stringToWatchFor = stringToWatchFor;
    }

    @Override
    public boolean test(String s) {

      if (foundString) {
        return true;
      }

      foundString = stringToWatchFor.equals(s);
      return false;
    }
}

And then use it like so:

List<String> a = Arrays.asList("a", "b", "c", "d", "e");
IsAfterString predicate = new IsAfterString("c");
List<String> list = a.stream().filter(predicate).collect(Collectors.toList());
list = predicate.hasFoundString() ? list : null;

System.out.println(list);

This will print the following statements:

  • Element E is "c" => [d,e]
  • Element E is "e" => []
  • Element E is "g" => null

Note that this predicate will not work if you use parallelStream.

GuiSim
  • 7,361
  • 6
  • 40
  • 50
  • 2
    Stateful predicates are a bad idea, and are outlawed by the specification of Stream. So not really a good idea to teach bad practices to someone who doesn't really know better. – Brian Goetz Oct 07 '16 at 21:48
  • @BrianGoetz Indeed, link to the spec https://docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.html#filter-java.util.function.Predicate- And the page on Statelessness https://docs.oracle.com/javase/8/docs/api/java/util/stream/package-summary.html#Statelessness – GuiSim Oct 07 '16 at 21:54