简体   繁体   中英

Java 8 Streams filter Strings by length

Can I use stream to check, which 2 consecutive strings have the biggest sum of their lengths?

For example I have 5 valid usernames and I should print only Johnny and Frank :

String line = "James Jack Johnny Frank Bob";
String regexForValidUserName = "[a-zA-Z][a-zA-Z0-9_]{2,24}";
Pattern patternForUserName = Pattern.compile(regexForValidUserName);
Matcher matcherForUserName = patternForUserName.matcher(line);
List<String> listOfUsers = new LinkedList<>();

if (matcherForUserName.find()) {
listOfUsers.add(matcherForUserName.group());
}

listOfUsers.stream().map((a,b,c,d) -> a.length + b.length > c.length + d.length).foreach(System.out::println);

Here's a stream based solution. I'm not sure I would use it over a for loop, but it does what you want with streams, and is relatively easy to understand.

As I said in the comment, the trick is to have a stream of pairs of names, instead of a stream of names.

    List<String> userNames = Arrays.asList("James", "Jack", "Johnny", "Frank", "Bob");
    List<String> longestPair =
        IntStream.range(0, userNames.size() - 1)
                 .mapToObj(i -> Arrays.asList(userNames.get(i), userNames.get(i + 1)))
                 .max(Comparator.comparing(pair -> pair.get(0).length() + pair.get(1).length()))
                 .orElseThrow(() -> new IllegalStateException("the list should have at least 2 elements"));

    System.out.println("longestPair = " + longestPair);

Please don't do that with a LinkedList, because random access to a linked list is very inefficient. But you should almost never use a linked list anyway. Prefer ArrayList. It's more efficient for basically all realistic use-cases.

You could also create a Pair class to make that more readable, instead of using a list of two elements.

To do that work you must break the original List<String> into List<Pair> chunks, then the work is very easy. and chunk2 is lazily just like as stream intermediate operations . for example:

Comparator<List<String>> length = comparing(pair -> { 
       return pair.get(0).length() + pair.get(1).length();
});

List<String> longest = chunk2(asList(line.split(" "))).max(length).get();
//           ^--- ["Johnny", "Frank"]

import java.util.Spliterators.AbstractSpliterator;
import static java.util.stream.StreamSupport.stream;
import static java.util.Spliterator.*;

<T> Stream<List<T>> chunk2(List<T> list) {
    int characteristics = ORDERED & SIZED & IMMUTABLE ;
    int size = list.size() - 1;
    return stream(new AbstractSpliterator<List<T>>(size, characteristics) {
        private int pos;

        @Override
        public boolean tryAdvance(Consumer<? super List<T>> action) {
            if (pos >= size) return false;

            action.accept(list.subList(pos, ++pos + 1));
            return true;
        }

    }, false);
}

Supporting such kind of operation for arbitrary streams (ie not having a source with random access that allows streaming over indices), requires a custom collector:

String line = "James Jack Johnny Frank Bob";
String regexForValidUserName = "[a-zA-Z][a-zA-Z0-9_]{2,24}";
Pattern patternForUserName = Pattern.compile(regexForValidUserName);
Matcher matcherForUserName = patternForUserName.matcher(line);
Stream.Builder<String> builder = Stream.builder();
while(matcherForUserName.find()) builder.add(matcherForUserName.group());
class State {
    String first, last, pair1, pair2;
    int currLength=-1;
    void add(String next) {
        if(first==null) first=next;
        else {
            int nextLength=last.length()+next.length();
                if(nextLength>currLength) {
                pair1=last;
                pair2=next;
                currLength=nextLength;
            }
        }
        last=next;
    }
    void merge(State next) {
        add(next.first);
        if(currLength<next.currLength) {
            pair1=next.pair1;
            pair2=next.pair2;
            currLength=next.currLength;
        }
        last=next.last;
    }
    String[] pair() {
        return currLength>=0? new String[]{ pair1, pair2 }: null;
    }
}
String[] str = builder.build()
       .collect(State::new, State::add, State::merge).pair();
System.out.println(Arrays.toString(str));

A Collector can have a mutable data structure which allows holding state like the previous element. To support merging of two such state objects, it also needs to track the first element, as the last element of one State object may form a pair with the first element of the next State object, if there is one.

So a loop would be simpler to program while the collector supports parallel processing, which only pays off if you have a really large number of elements.

The stream creation itself would be more straight-forward if we had Java 9's factory method already:

String line = "James Jack Johnny Frank Bob";
String regexForValidUserName = "[a-zA-Z][a-zA-Z0-9_]{2,24}";
Pattern patternForUserName = Pattern.compile(regexForValidUserName);
String[] str = patternForUserName.matcher(line).results()
    .map(MatchResult::group)
    .collect(State::new, State::add, State::merge).pair();
System.out.println(Arrays.toString(str));

(The State class wouldn't change)

You could iterate over the stream with .forEach , using a custom mutable data structure to track the longest pairs, for example:

class Tracker {
  List<String> pairs = Collections.emptyList();
  String prev = "";
  int longest = 0;

  public void check(String name) {
    int length = prev.length() + name.length();
    if (length > longest) {
      longest = length;
      pairs = Arrays.asList(prev, name);
    }
    prev = name;
  }

  public List<String> pairs() {
    return pairs;
  }
}

String line = "James Jack Johnny Frank Bob";

Tracker tracker = new Tracker();
Stream.of(line.split(" ")).forEach(tracker::check);

System.out.println(tracker.pairs());

This will print [Johnny, Frank] .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM