簡體   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