簡體   English   中英

如何將字符串流轉換為字符串流對?

[英]How can I convert a Stream of Strings to Stream of String pairs?

我想取一串字符串並將其轉換為單詞對流。 例如:

我有: { "A", "Apple", "B", "Banana", "C", "Carrot" }

我想要: { ("A", "Apple"), ("Apple", "B"), ("B", "Banana"), ("Banana", "C") }

這與使用帶有lambda的JDK8的Zipping流中概述的Zipping幾乎相同(java.util.stream.Streams.zip)

然而,這產生: { (A, Apple), (B, Banana), (C, Carrot) }

以下代碼有效,但顯然是錯誤的方法(不是線程安全等):

static String buffered = null;

static void output(String s) {
    String result = null;
    if (buffered != null) {
        result = buffered + "," + s;
    } else {
        result = null;
    }

    buffered = s;
    System.out.println(result);
}

// ***** 

Stream<String> testing = Stream.of("A", "Apple", "B", "Banana", "C", "Carrot");
testing.forEach(s -> {output(s);});

這應該做你想要的,基於@ njzk2的兩次使用流的注釋,跳過第二種情況下的第一個元素。 它使用您在原始問題中鏈接的zip方法。

public static void main(String[] args) {
  List<String> input = Arrays.asList("A", "Apple", "B", "Banana", "C", "Carrot");
  List<List<String>> paired = zip(input.stream(),
                                  input.stream().skip(1),
                                  (a, b) -> Arrays.asList(a, b))
                              .collect(ArrayList::new, ArrayList::add, ArrayList::addAll);
  System.out.println(paired);
}

這將輸出List<List<String>> ,其內容為:

[[A, Apple], [Apple, B], [B, Banana], [Banana, C], [C, Carrot]]

在評論中,如果您已有Stream ,則詢問如何執行此操作。 不幸的是,這是困難的,因為Streams是無狀態的,並沒有真正在的“相鄰”元素的概念Stream 這里有一個很好的討論

我可以想到兩種方法,但我不認為你會喜歡它們中的任何一種:

  1. Stream轉換為List ,然后執行上面的解決方案。 丑陋,但只要Stream不是無限的,並且性能無關緊要。
  2. 使用@ TagirValeev的答案 ,只要您使用的是StreamEx而不是Stream ,並且願意在第三方庫中添加依賴項。

與此討論相關的還有以下問題: 我可以在Java 8中復制Stream嗎? ; 這對你的問題不是好消息,但值得一讀,可能會有一個對你更有吸引力的解決方案。

如果你:

  1. 不喜歡使用流中的所有字符串創建列表的想法
  2. 不想使用外部庫
  3. 喜歡弄臟你的手

然后,您可以創建一個方法,使用Java 8低級流構建器StreamSupportSpliterator

class StreamUtils {
    public static<T> Stream<List<T>> sliding(int size, Stream<T> stream) {
        return sliding(size, 1, stream);
    }

    public static<T> Stream<List<T>> sliding(int size, int step, Stream<T> stream) {
        Spliterator<T> spliterator = stream.spliterator();
        long estimateSize;

        if (!spliterator.hasCharacteristics(Spliterator.SIZED)) {
            estimateSize = Long.MAX_VALUE;
        } else if (size > spliterator.estimateSize()) {
            estimateSize = 0;
        } else {
            estimateSize = (spliterator.estimateSize() - size) / step + 1;
        }

        return StreamSupport.stream(
                new Spliterators.AbstractSpliterator<List<T>>(estimateSize, spliterator.characteristics()) {
                    List<T> buffer = new ArrayList<>(size);

                    @Override
                    public boolean tryAdvance(Consumer<? super List<T>> consumer) {
                        while (buffer.size() < size && spliterator.tryAdvance(buffer::add)) {
                            // Nothing to do
                        }

                        if (buffer.size() == size) {
                            List<T> keep = new ArrayList<>(buffer.subList(step, size));
                            consumer.accept(buffer);
                            buffer = keep;
                            return true;
                        }
                        return false;
                    }
                }, stream.isParallel());
    }
}

方法和參數命名的靈感來自他們的Scala對應物。

我們來測試一下:

Stream<String> testing = Stream.of("A", "Apple", "B", "Banana", "C", "Carrot");
System.out.println(StreamUtils.sliding(2, testing).collect(Collectors.toList()));

[[A,Apple],[Apple,B],[B,Banana],[香蕉,C],[C,胡蘿卜]]

不重復元素怎么樣:

Stream<String> testing = Stream.of("A", "Apple", "B", "Banana", "C", "Carrot");
System.out.println(StreamUtils.sliding(2, 2, testing).collect(Collectors.toList()));

[[A,Apple],[B,香蕉],[C,胡蘿卜]]

現在有一個無限的Stream

StreamUtils.sliding(5, Stream.iterate(0, n -> n + 1))
        .limit(5)
        .forEach(System.out::println);

[0,1,2,3,4]
[1,2,3,4,5]
[2,3,4,5,6]
[3,4,5,6,7]
[4,5,6,7,8]

您可以使用我的StreamEx庫來增強標准Stream API。 有一個方法pairMap可以完全滿足您的需求:

StreamEx.of("A", "Apple", "B", "Banana", "C", "Carrot")
        .pairMap((a, b) -> a+","+b)
        .forEach(System.out::println);

輸出:

A,Apple
Apple,B
B,Banana
Banana,C
C,Carrot

pairMap參數是將相鄰元素對轉換為適合您需要的元素的函數。 如果項目中有Pair類,則可以使用.pairMap(Pair::new)來獲取對的流。 如果要創建雙元素列表流,可以使用:

List<List<String>> list = StreamEx.of("A", "Apple", "B", "Banana", "C", "Carrot")
                                    .pairMap((a, b) -> StreamEx.of(a, b).toList())
                                    .toList();
System.out.println(list); // [[A, Apple], [Apple, B], [B, Banana], [Banana, C], [C, Carrot]]

這適用於任何元素源(您可以使用StreamEx.of(collection)StreamEx.of(stream)等),如果您在pairMap之前有更多流操作並且對並行處理非常友好,則正確工作(與涉及流的解決方案不同)荏苒)。

如果您的輸入是具有快速隨機訪問權限的List並且您實際上希望List<List<String>>作為結果,那么使用ofSubLists在我的庫中實現此目的的方式更短且有所不同:

List<String> input = Arrays.asList("A", "Apple", "B", "Banana", "C", "Carrot");
List<List<String>> list = StreamEx.ofSubLists(input, 2, 1).toList();
System.out.println(list); // [[A, Apple], [Apple, B], [B, Banana], [Banana, C], [C, Carrot]]

在幕后input.subList(i, i+2)為每個輸入列表位置調用input.subList(i, i+2) ,因此不會將數據復制到新列表,而是創建引用原始列表的子列表。

這是創建對的List<List<String>>的最少量代碼:

List<List<String>> pairs = new LinkedList<>();
testing.reduce((a, b)-> {pairs.add(Arrays.asList(a,b)); return b;});

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM