繁体   English   中英

Java 8 Streams按长度过滤字符串

[英]Java 8 Streams filter Strings by length

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

例如,我有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);

这是一个基于流的解决方案。 我不确定我是否会在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类,使其更具可读性,而不是使用两个元素的列表。

要完成这项工作,您必须将原始List<String>分解为List<Pair>块,然后工作非常简单。 chunk2一样懒惰 ,就像流中间操作一样 例如:

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

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

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的工厂方法,那么流创建本身会更直接:

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级不会改变)

您可以使用.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]

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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