Java 8 Streams按长度过滤字符串

时间:2017-07-02 14:47:21

标签: java java-8 java-stream

我可以使用流来检查哪两个连续字符串的长度最大?

例如,我有5个有效的用户名,我应该只打印JohnnyFrank

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);

4 个答案:

答案 0 :(得分:2)

这是基于流的解决方案。我不确定我是否会在for循环中使用它,但它可以使用流做你想要的,并且相对容易理解。

正如我在评论中所说,诀窍是拥有一对名称流,而不是名称流。

    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);

请不要使用LinkedList执行此操作,因为对链表的随机访问效率非常低。但是你几乎不应该使用链表。首选ArrayList。对于基本上所有真实的用例来说,它更有效。

您还可以创建一个Pair类,使其更具可读性,而不是使用两个元素的列表。

答案 1 :(得分:1)

要完成这项工作,您必须将原始的List<String>分解为List<Pair>块,然后才能轻松完成工作。而chunk2 懒惰就像流intermediate operations一样。例如:

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);
}

答案 2 :(得分:1)

支持任意流的这种操作(即,没有允许通过索引进行流式传输的随机访问源),需要一个自定义收集器:

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));

收集器可以具有可变数据结构,允许保持状态,如前一个元素。为了支持合并两个这样的状态对象,它还需要跟踪第一个元素,因为一个State对象的最后一个元素可能与下一个State对象的第一个元素形成一对,如果有的话是一个。

因此,当收集器支持并行处理时,循环将更容易编程,只有当你有非常多的元素时才会得到回报。

如果我们已经Java 9’s factory method

,则流创建本身会更直截了当
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));

State类不会改变)

答案 3 :(得分:0)

您可以使用.forEach迭代流,使用自定义可变数据结构来跟踪最长的对,例如:

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());

这将打印[Johnny, Frank]