ลำดับการสตรีม Java 8 แบบย้อนกลับ


153

คำถามทั่วไป: อะไรคือวิธีที่เหมาะสมในการย้อนกลับสตรีม สมมติว่าเราไม่ทราบว่าองค์ประกอบใดของสตรีมที่ประกอบไปด้วยอะไรคือวิธีทั่วไปในการย้อนกลับสตรีมใด ๆ

คำถามเฉพาะ:

IntStreamนำเสนอวิธีการแบบช่วงเพื่อสร้างจำนวนเต็มในช่วงที่เฉพาะเจาะจงIntStream.range(-range, 0)ตอนนี้ฉันต้องการย้อนกลับจากการเปลี่ยนช่วงจาก 0 ไปเป็นค่าลบจะไม่ทำงานและฉันไม่สามารถใช้Integer::compare

List<Integer> list = Arrays.asList(1,2,3,4);
list.stream().sorted(Integer::compare).forEach(System.out::println);

กับIntStreamฉันจะได้รับรวบรวมข้อผิดพลาดนี้

ข้อผิดพลาด: (191, 0) ajc: วิธีการsorted()ในประเภทIntStreamไม่สามารถใช้ได้กับข้อโต้แย้ง ( Integer::compare)

ฉันคิดถึงอะไรที่นี่


1
IntStreamไม่มี.sorted(Comparator)วิธี; คุณจะต้องไปผ่านStream<Integer>ครั้งแรกและย้อนกลับก่อนที่จะมีผลผลิตIntStream
FGE

ตกลงฉันได้พบแล้วคุณต้องใช้. boxed () และ .sorted ()
vach

4
ในการสร้างเพื่อย้อนกลับทำสิ่งที่ชอบIntStream.range(0, n) map(i -> n - i - 1)ไม่จำเป็นต้องทำมวยและเรียงลำดับ
Stuart Marks

1
คำถาม gerneral ของคุณและคำถามเฉพาะของคุณอ่านเหมือนสองคำถามที่สมบูรณ์แบบที่แตกต่างกันให้ฉัน ทั่วไปพูดถึงการย้อนกลับของกระแสในขณะที่เฉพาะพูดถึงหมายเลขการสั่งซื้อในลำดับถัดลงมา หากสตรีมผลิตตัวเลขในลักษณะที่ไม่ได้เรียงลำดับ1, 3, 2แล้วผลลัพธ์ที่คุณคาดหวังคืออะไร คุณต้องการสตรีมแบบกลับด้าน2, 3, 1หรือแบบสตรีมที่เรียงกลับกัน3, 2, 1ไหม
chiccodoro

8
คุณไม่สามารถย้อนกลับสตรีมโดยทั่วไปได้ตัวอย่างเช่นสตรีมอาจไม่สิ้นสุด
assylias

คำตอบ:


78

สำหรับคำถามที่เฉพาะเจาะจงในการสร้างสิ่งที่ตรงกันข้ามIntStreamลองทำดังนี้:

static IntStream revRange(int from, int to) {
    return IntStream.range(from, to)
                    .map(i -> to - i + from - 1);
}

หลีกเลี่ยงการชกมวยและการเรียงลำดับ

สำหรับคำถามทั่วไปเกี่ยวกับวิธีย้อนกลับสตรีมประเภทใดฉันไม่รู้ว่ามีวิธี "เหมาะสม" มีหลายวิธีที่ฉันสามารถคิดได้ ท้ายที่สุดทั้งการจัดเก็บองค์ประกอบของกระแส ฉันไม่รู้วิธีที่จะย้อนกลับกระแสโดยไม่ต้องจัดเก็บองค์ประกอบ

วิธีแรกนี้จะเก็บองค์ประกอบไว้ในอาร์เรย์และอ่านออกไปยังสตรีมในลำดับที่กลับกัน โปรดทราบว่าเนื่องจากเราไม่ทราบประเภทรันไทม์ขององค์ประกอบสตรีมเราจึงไม่สามารถพิมพ์อาร์เรย์ได้อย่างถูกต้อง

@SuppressWarnings("unchecked")
static <T> Stream<T> reverse(Stream<T> input) {
    Object[] temp = input.toArray();
    return (Stream<T>) IntStream.range(0, temp.length)
                                .mapToObj(i -> temp[temp.length - i - 1]);
}

อีกเทคนิคหนึ่งที่ใช้นักสะสมเพื่อสะสมรายการลงในรายการที่กลับรายการ นี่เป็นการแทรกจำนวนมากที่ด้านหน้าของArrayListวัตถุดังนั้นจึงมีการคัดลอกเกิดขึ้นมากมาย

Stream<T> input = ... ;
List<T> output =
    input.collect(ArrayList::new,
                  (list, e) -> list.add(0, e),
                  (list1, list2) -> list1.addAll(0, list2));

อาจเป็นไปได้ที่จะเขียนตัวรวบรวมการย้อนกลับที่มีประสิทธิภาพมากขึ้นโดยใช้โครงสร้างข้อมูลแบบกำหนดเองบางชนิด

อัพเดท 2016-01-29

เนื่องจากคำถามนี้ได้รับความสนใจเล็กน้อยเมื่อเร็ว ๆ นี้ฉันคิดว่าฉันควรอัปเดตคำตอบของฉันเพื่อแก้ปัญหาด้วยการแทรกที่ด้านหน้าของ ArrayListนี้ผมคิดว่าฉันควรปรับปรุงคำตอบของฉันที่จะแก้ปัญหาด้วยการใส่ที่ด้านหน้าของ สิ่งนี้จะไม่มีประสิทธิภาพอย่างน่ากลัวด้วยองค์ประกอบจำนวนมากซึ่งต้องใช้การคัดลอก O (N ^ 2)

ควรใช้ArrayDequeแทนซึ่งสนับสนุนการแทรกที่ด้านหน้าได้อย่างมีประสิทธิภาพ รอยย่นขนาดเล็กคือเราไม่สามารถใช้รูปแบบสามหาเรื่องของStream.collect(); มันต้องใช้เนื้อหาของหาเรื่องที่สองจะรวมเข้าหาเรื่องแรกและไม่มี "add-ทั้งหมดที่หน้า" Dequeการดำเนินการเป็นกลุ่มบน แต่เราใช้addAll()เพื่อเพิ่มเนื้อหาของอาร์กิวเมนต์แรกไปยังจุดสิ้นสุดของวินาทีและจากนั้นเราจะส่งกลับสอง สิ่งนี้ต้องใช้Collector.of()วิธีการจากโรงงาน

รหัสที่สมบูรณ์คือ:

Deque<String> output =
    input.collect(Collector.of(
        ArrayDeque::new,
        (deq, t) -> deq.addFirst(t),
        (d1, d2) -> { d2.addAll(d1); return d2; }));

ผลลัพธ์คือDequeแทนที่จะเป็น a Listแต่ไม่น่าเป็นปัญหามากนักเนื่องจากสามารถวนซ้ำหรือสตรีมได้อย่างง่ายดายในลำดับที่กลับด้านตอนนี้


15
อีกทางเลือกหนึ่ง:IntStream.iterate(to-1, i->i-1).limit(to-from)
Holger

2
@ โฮลเกอร์ แต่น่าเสียดายที่โซลูชั่นดังกล่าวไม่สามารถจัดการกับการล้นได้อย่างถูกต้อง
แบรนดอน

2
@Brandon Mintern: แน่นอนคุณจะต้องใช้.limit(endExcl-(long)startIncl)แทน แต่สำหรับลำธารขนาดใหญ่เช่นนั้นมันก็ท้อแท้มากเพราะมันมีประสิทธิภาพน้อยกว่าrangeโซลูชั่นพื้นฐาน ในขณะที่ฉันเขียนความคิดเห็นฉันไม่ทราบเกี่ยวกับความแตกต่างของประสิทธิภาพ
Holger

48

ทางออกที่สง่างาม

List<Integer> list = Arrays.asList(1,2,3,4);
list.stream()
    .boxed() // Converts Intstream to Stream<Integer>
    .sorted(Collections.reverseOrder()) // Method on Stream<Integer>
    .forEach(System.out::println);

6
เป็นสง่า แต่ไม่ทำงานอย่างเต็มที่ตามที่ดูเหมือนว่าองค์ประกอบของรายการจะต้องComparable...
Krzysztof Wolny

26
นั่นคือสมมติว่าเราต้องการให้องค์ประกอบเรียงลำดับย้อนกลับ คำถามคือเกี่ยวกับการกลับคำสั่งของกระแส
Dillon Ryan Redding

37

โซลูชันจำนวนมากที่นี่เรียงลำดับหรือย้อนกลับIntStreamแต่จำเป็นต้องใช้ที่เก็บข้อมูลระดับกลางโดยไม่จำเป็น วิธีการแก้ปัญหาของ Stuart Marksเป็นวิธีที่จะไป:

static IntStream revRange(int from, int to) {
    return IntStream.range(from, to).map(i -> to - i + from - 1);
}

สามารถจัดการกับการล้นได้อย่างถูกต้องผ่านการทดสอบนี้:

@Test
public void testRevRange() {
    assertArrayEquals(revRange(0, 5).toArray(), new int[]{4, 3, 2, 1, 0});
    assertArrayEquals(revRange(-5, 0).toArray(), new int[]{-1, -2, -3, -4, -5});
    assertArrayEquals(revRange(1, 4).toArray(), new int[]{3, 2, 1});
    assertArrayEquals(revRange(0, 0).toArray(), new int[0]);
    assertArrayEquals(revRange(0, -1).toArray(), new int[0]);
    assertArrayEquals(revRange(MIN_VALUE, MIN_VALUE).toArray(), new int[0]);
    assertArrayEquals(revRange(MAX_VALUE, MAX_VALUE).toArray(), new int[0]);
    assertArrayEquals(revRange(MIN_VALUE, MIN_VALUE + 1).toArray(), new int[]{MIN_VALUE});
    assertArrayEquals(revRange(MAX_VALUE - 1, MAX_VALUE).toArray(), new int[]{MAX_VALUE - 1});
}

น่ากลัวและเรียบง่าย มาจากโอเพนซอร์สบ้างไหม (สิ่ง Estreams) หรือบางส่วนของรหัสของคุณ?
vach

ขอบคุณ! น่าเสียดายที่ฉันไม่ได้ตั้งใจจะรั่วไหลEstreamsชื่อ (ฉันจะลบมันออกจากโพสต์) มันเป็นหนึ่งใน บริษัท ของเราเรียนภายในยูทิลิตี้ที่เราใช้ในการเสริมjava.util.stream.Stream's staticวิธี
แบรนดอน

3
ตกลง…“ ยอดเยี่ยมและเรียบง่าย” …มีกรณีใดบ้างที่โซลูชันนี้จัดการซึ่งโซลูชันที่ง่ายยิ่งขึ้นของ Stuart Marksไม่ได้จัดการมากกว่าหนึ่งปีครึ่งแล้ว
Holger

ฉันเพิ่งทดสอบวิธีแก้ปัญหาของเขาด้วยวิธีทดสอบของฉันด้านบน มันผ่านไป ฉันหลีกเลี่ยงการล้นเกินโดยไม่จำเป็นแทนที่จะยอมรับมันอย่างที่เขาทำ ฉันเห็นด้วยว่าเขาดีกว่า ฉันจะแก้ไขของฉันเพื่อสะท้อนสิ่งนั้น
Brandon

1
@vach คุณสามารถใช้โดยการStreamExระบุขั้นตอน:IntStreamEx.rangeClosed(from-1, to, -1)
Tagir Valeev

34

คำถามทั่วไป:

สตรีมไม่ได้จัดเก็บองค์ประกอบใด ๆ

ดังนั้นการวนซ้ำองค์ประกอบในลำดับย้อนกลับจึงเป็นไปไม่ได้หากไม่มีการจัดเก็บองค์ประกอบในการรวบรวมกลางบางอย่าง

Stream.of("1", "2", "20", "3")
      .collect(Collectors.toCollection(ArrayDeque::new)) // or LinkedList
      .descendingIterator()
      .forEachRemaining(System.out::println);

อัปเดต: เปลี่ยน LinkedList เป็น ArrayDeque (ดีกว่า) ดูที่นี่สำหรับรายละเอียด

พิมพ์:

3

20

2

1

โดยวิธีการใช้sortวิธีการที่ไม่ถูกต้องตามที่มันเรียงลำดับไม่ย้อนกลับ (สมมติว่าสตรีมอาจมีองค์ประกอบที่ไม่ได้เรียงลำดับ)

คำถามเฉพาะ:

ฉันพบสิ่งนี้ง่ายง่ายและใช้งานง่าย ( ความคิดเห็นที่คัดลอก @ โฮลเกอร์)

IntStream.iterate(to - 1, i -> i - 1).limit(to - from)

3
การดำเนินการสตรีมบางอย่างเช่นsortedและdistinctจัดเก็บผลลัพธ์ระดับกลางจริง ๆ ดูเอกสาร API ของแพ็คเกจสำหรับข้อมูลบางอย่างเกี่ยวกับเรื่องนั้น
Lii

@Lii ฉันเห็นNo storageในหน้าเดียวกัน แม้ว่าจะเป็นร้านค้าเราก็ไม่สามารถเข้าถึงที่จัดเก็บข้อมูลได้ (เช่นกันNo storageฉันก็สบายดี)
Venkata Raju

คำตอบที่ดี. แต่เนื่องจากมีการใช้พื้นที่เพิ่มเติมจึงไม่เป็นความคิดที่ดีสำหรับโปรแกรมเมอร์ที่จะใช้แนวทางของคุณในการรวบรวมขนาดใหญ่มาก
มนู Manjunath

ฉันชอบความเรียบง่ายของโซลูชันจากมุมมองที่เข้าใจได้และใช้ประโยชน์จากวิธีการที่มีอยู่ในโครงสร้างข้อมูลที่มีอยู่ ... วิธีแก้ไขปัญหาก่อนหน้าด้วยการนำแผนที่ไปใช้นั้นยากกว่าที่จะเข้าใจ แต่แน่นอนว่ามนูถูกต้องสำหรับคอลเลกชันขนาดใหญ่ ใช้งานง่ายและจะเลือกแผนที่หนึ่งข้างต้น
Beezer

นี่เป็นหนึ่งในคำตอบที่ถูกต้องไม่กี่ที่นี่ คนอื่น ๆ ส่วนใหญ่ไม่ได้ทำการสตรีมกลับกัน แต่พวกเขาพยายามหลีกเลี่ยงที่จะทำอย่างใดอย่างหนึ่ง (ซึ่งใช้ได้เฉพาะในสถานการณ์ที่แปลกประหลาดซึ่งปกติแล้วคุณจะไม่จำเป็นต้องย้อนกลับในตอนแรก) หากคุณพยายามย้อนกลับกระแสที่ไม่พอดีกับหน่วยความจำคุณกำลังทำผิดอยู่ดี ดัมพ์ลงใน DB และรับสตรีมที่กลับด้านโดยใช้ SQL ปกติ
ลูกบาศก์

19

ไม่มี lib ภายนอก ...

import java.util.List;
import java.util.Collections;
import java.util.stream.Collector;

public class MyCollectors {

    public static <T> Collector<T, ?, List<T>> toListReversed() {
        return Collectors.collectingAndThen(Collectors.toList(), l -> {
            Collections.reverse(l);
            return l;
        });
    }

}

15

หากดำเนินการComparable<T>(อดีต. Integer, String, Date) Comparator.reverseOrder()คุณสามารถทำได้โดยใช้

List<Integer> list = Arrays.asList(1, 2, 3, 4);
list.stream()
     .sorted(Comparator.reverseOrder())
     .forEach(System.out::println);

6
นี่ไม่ได้ย้อนกระแส เรียงลำดับสตรีมตามลำดับย้อนกลับ ดังนั้นหากคุณมีStream.of(1,3,2)ผลลัพธ์จะStream.of(3,2,1)ไม่เป็นเช่นนั้นStream.of(2,3,1)
wilmol

12

คุณสามารถกำหนดตัวสะสมของคุณเองที่รวบรวมองค์ประกอบในลำดับย้อนกลับ:

public static <T> Collector<T, List<T>, List<T>> inReverse() {
    return Collector.of(
        ArrayList::new,
        (l, t) -> l.add(t),
        (l, r) -> {l.addAll(r); return l;},
        Lists::<T>reverse);
}

และใช้มันเช่น:

stream.collect(inReverse()).forEach(t -> ...)

ฉันใช้ ArrayList เพื่อส่งต่อการแทรกรวบรวมรายการ (ตอนท้ายของรายการ) และรายการ Guava อย่างมีประสิทธิภาพเพื่อให้มุมมองย้อนกลับของรายการได้อย่างมีประสิทธิภาพโดยไม่ต้องคัดลอกรายการอื่น

นี่คือกรณีทดสอบบางอย่างสำหรับตัวรวบรวมที่กำหนดเอง:

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.*;

import java.util.ArrayList;
import java.util.List;
import java.util.function.BiConsumer;
import java.util.function.BinaryOperator;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collector;

import org.hamcrest.Matchers;
import org.junit.Test;

import com.google.common.collect.Lists;

public class TestReverseCollector {
    private final Object t1 = new Object();
    private final Object t2 = new Object();
    private final Object t3 = new Object();
    private final Object t4 = new Object();

    private final Collector<Object, List<Object>, List<Object>> inReverse = inReverse();
    private final Supplier<List<Object>> supplier = inReverse.supplier();
    private final BiConsumer<List<Object>, Object> accumulator = inReverse.accumulator();
    private final Function<List<Object>, List<Object>> finisher = inReverse.finisher();
    private final BinaryOperator<List<Object>> combiner = inReverse.combiner();

    @Test public void associative() {
        final List<Object> a1 = supplier.get();
        accumulator.accept(a1, t1);
        accumulator.accept(a1, t2);
        final List<Object> r1 = finisher.apply(a1);

        final List<Object> a2 = supplier.get();
        accumulator.accept(a2, t1);
        final List<Object> a3 = supplier.get();
        accumulator.accept(a3, t2);
        final List<Object> r2 = finisher.apply(combiner.apply(a2, a3));

        assertThat(r1, Matchers.equalTo(r2));
    }

    @Test public void identity() {
        final List<Object> a1 = supplier.get();
        accumulator.accept(a1, t1);
        accumulator.accept(a1, t2);
        final List<Object> r1 = finisher.apply(a1);

        final List<Object> a2 = supplier.get();
        accumulator.accept(a2, t1);
        accumulator.accept(a2, t2);
        final List<Object> r2 = finisher.apply(combiner.apply(a2, supplier.get()));

        assertThat(r1, equalTo(r2));
    }

    @Test public void reversing() throws Exception {
        final List<Object> a2 = supplier.get();
        accumulator.accept(a2, t1);
        accumulator.accept(a2, t2);

        final List<Object> a3 = supplier.get();
        accumulator.accept(a3, t3);
        accumulator.accept(a3, t4);

        final List<Object> r2 = finisher.apply(combiner.apply(a2, a3));

        assertThat(r2, contains(t4, t3, t2, t1));
    }

    public static <T> Collector<T, List<T>, List<T>> inReverse() {
        return Collector.of(
            ArrayList::new,
            (l, t) -> l.add(t),
            (l, r) -> {l.addAll(r); return l;},
            Lists::<T>reverse);
    }
}

9

StreamUtils ตอบสนองไซคลอปมีวิธีการสตรีมย้อนกลับ ( javadoc )

  StreamUtils.reverse(Stream.of("1", "2", "20", "3"))
             .forEach(System.out::println);

มันทำงานได้โดยการรวบรวมไปยัง ArrayList แล้วใช้คลาส ListIterator ซึ่งสามารถวนซ้ำในทิศทางใดทิศทางหนึ่งเพื่อย้ำไปทางด้านหลังของรายการ

หากคุณมีรายการอยู่แล้วมันจะมีประสิทธิภาพมากขึ้น

  StreamUtils.reversedStream(Arrays.asList("1", "2", "20", "3"))
             .forEach(System.out::println);

1
Nice ไม่รู้เกี่ยวกับโครงการนี้
vach

1
ไซคลอปในขณะนี้ยังมาพร้อมกับSpliteratorsสำหรับการกลับรายการสตรีมที่มีประสิทธิภาพ (ปัจจุบันสำหรับ Ranges, Array และ Lists) การสร้างส่วนขยาย cyclops SequenceM Stream ด้วย SequenceM.of, SequenceM.range หรือ SequenceM.fromList จะใช้ประโยชน์จากตัวแยกสัญญาณแบบย้อนกลับได้อย่างมีประสิทธิภาพโดยอัตโนมัติ
John McClean

6

ฉันขอแนะนำให้ใช้jOOλมันเป็นห้องสมุดที่ยอดเยี่ยมที่เพิ่มฟังก์ชันการทำงานที่มีประโยชน์มากมายให้กับ Java 8 สตรีมและ lambdas

จากนั้นคุณสามารถทำสิ่งต่อไปนี้:

List<Integer> list = Arrays.asList(1,2,3,4);    
Seq.seq(list).reverse().forEach(System.out::println)

เรียบง่ายเหมือนที่ มันเป็นไลบรารี่ที่มีน้ำหนักเบาและน่าลองเพิ่มเข้ากับโปรเจ็กต์ Java 8


5

นี่คือวิธีแก้ปัญหาที่ฉันได้รับ:

private static final Comparator<Integer> BY_ASCENDING_ORDER = Integer::compare;
private static final Comparator<Integer> BY_DESCENDING_ORDER = BY_ASCENDING_ORDER.reversed();

จากนั้นใช้เครื่องมือเปรียบเทียบเหล่านี้:

IntStream.range(-range, 0).boxed().sorted(BY_DESCENDING_ORDER).forEach(// etc...

2
นี่เป็นเพียงคำตอบสำหรับ "คำถามเฉพาะ" ของคุณ แต่ไม่ใช่สำหรับ "คำถามทั่วไป" ของคุณ
chiccodoro

9
Collections.reverseOrder()มีอยู่ตั้งแต่ Java 1.2 และทำงานร่วมกับInteger...
Holger

4

วิธีการเกี่ยวกับอรรถประโยชน์นี้

public static <T> Stream<T> getReverseStream(List<T> list) {
    final ListIterator<T> listIt = list.listIterator(list.size());
    final Iterator<T> reverseIterator = new Iterator<T>() {
        @Override
        public boolean hasNext() {
            return listIt.hasPrevious();
        }

        @Override
        public T next() {
            return listIt.previous();
        }
    };
    return StreamSupport.stream(Spliterators.spliteratorUnknownSize(
            reverseIterator,
            Spliterator.ORDERED | Spliterator.IMMUTABLE), false);
}

ดูเหมือนว่าจะทำงานกับทุกกรณีโดยไม่มีการทำซ้ำ


ฉันชอบวิธีนี้มาก คำตอบส่วนใหญ่แบ่งออกเป็นสองประเภท: (1) ย้อนกลับการรวบรวมและ. stream () มัน (2) อุทธรณ์ไปยังนักสะสมที่กำหนดเอง ทั้งสองอย่างนั้นไม่จำเป็นอย่างยิ่ง มิฉะนั้นจะเป็นพยานเกี่ยวกับปัญหาเรื่องภาษาที่ร้ายแรงใน JDK 8 เอง และคำตอบของคุณคือสิ่งที่ตรงกันข้าม :)
vitrums


3

วิธีที่ง่ายที่สุด (การรวบรวมอย่างง่าย - รองรับการสตรีมแบบขนาน):

public static <T> Stream<T> reverse(Stream<T> stream) {
    return stream
            .collect(Collector.of(
                    () -> new ArrayDeque<T>(),
                    ArrayDeque::addFirst,
                    (q1, q2) -> { q2.addAll(q1); return q2; })
            )
            .stream();
}

วิธีขั้นสูง (รองรับสตรีมแบบขนานในวิธีต่อเนื่อง):

public static <T> Stream<T> reverse(Stream<T> stream) {
    Objects.requireNonNull(stream, "stream");

    class ReverseSpliterator implements Spliterator<T> {
        private Spliterator<T> spliterator;
        private final Deque<T> deque = new ArrayDeque<>();

        private ReverseSpliterator(Spliterator<T> spliterator) {
            this.spliterator = spliterator;
        }

        @Override
        @SuppressWarnings({"StatementWithEmptyBody"})
        public boolean tryAdvance(Consumer<? super T> action) {
            while(spliterator.tryAdvance(deque::addFirst));
            if(!deque.isEmpty()) {
                action.accept(deque.remove());
                return true;
            }
            return false;
        }

        @Override
        public Spliterator<T> trySplit() {
            // After traveling started the spliterator don't contain elements!
            Spliterator<T> prev = spliterator.trySplit();
            if(prev == null) {
                return null;
            }

            Spliterator<T> me = spliterator;
            spliterator = prev;
            return new ReverseSpliterator(me);
        }

        @Override
        public long estimateSize() {
            return spliterator.estimateSize();
        }

        @Override
        public int characteristics() {
            return spliterator.characteristics();
        }

        @Override
        public Comparator<? super T> getComparator() {
            Comparator<? super T> comparator = spliterator.getComparator();
            return (comparator != null) ? comparator.reversed() : null;
        }

        @Override
        public void forEachRemaining(Consumer<? super T> action) {
            // Ensure that tryAdvance is called at least once
            if(!deque.isEmpty() || tryAdvance(action)) {
                deque.forEach(action);
            }
        }
    }

    return StreamSupport.stream(new ReverseSpliterator(stream.spliterator()), stream.isParallel());
}

หมายเหตุคุณสามารถขยายไปยังสตรีมประเภทอื่น ๆ ได้อย่างรวดเร็ว (IntStream, ... )

การทดสอบ:

// Use parallel if you wish only
revert(Stream.of("One", "Two", "Three", "Four", "Five", "Six").parallel())
    .forEachOrdered(System.out::println);

ผล:

Six
Five
Four
Three
Two
One

หมายเหตุเพิ่มเติม:simplest wayมันไม่เป็นประโยชน์ดังนั้นเมื่อนำมาใช้กับการดำเนินงานสตรีมอื่น ๆ (คอลเล็กชันเข้าร่วมแบ่งขนาน) advance wayไม่ได้มีปัญหาที่และมันช่วยให้ยังเป็นลักษณะเริ่มต้นของกระแสเช่นSORTEDและอื่น ๆ มันเป็นวิธีที่จะไปใช้กับการดำเนินงานอื่น ๆ หลังจากกระแสย้อนกลับ


2

หนึ่งสามารถเขียนสะสมที่รวบรวมองค์ประกอบในลำดับที่กลับรายการ:

public static <T> Collector<T, ?, Stream<T>> reversed() {
    return Collectors.collectingAndThen(Collectors.toList(), list -> {
        Collections.reverse(list);
        return list.stream();
    });
}

และใช้มันเช่นนี้

Stream.of(1, 2, 3, 4, 5).collect(reversed()).forEach(System.out::println);

คำตอบเดิม (มีข้อบกพร่อง - มันไม่ทำงานอย่างถูกต้องสำหรับกระแสข้อมูลแบบขนาน):

วิธีการย้อนกลับสตรีมวัตถุประสงค์ทั่วไปอาจมีลักษณะดังนี้:

public static <T> Stream<T> reverse(Stream<T> stream) {
    LinkedList<T> stack = new LinkedList<>();
    stream.forEach(stack::push);
    return stack.stream();
}

2

ไม่ใช้ Java8 เพียงอย่างเดียว แต่หากคุณใช้วิธี Lists.reverse () ของฝรั่งคุณสามารถใช้วิธีนี้ได้อย่างง่ายดาย:

List<Integer> list = Arrays.asList(1,2,3,4);
Lists.reverse(list).stream().forEach(System.out::println);

2

โดยคำนึงถึงคำถามที่เฉพาะเจาะจงในการสร้างสิ่งที่ตรงกันข้ามIntStream:

เริ่มต้นจากJava 9คุณสามารถใช้เวอร์ชันสามอาร์กิวเมนต์ของIntStream.iterate(...):

IntStream.iterate(10, x -> x >= 0, x -> x - 1).forEach(System.out::println);

// Out: 10 9 8 7 6 5 4 3 2 1 0

ที่อยู่:

IntStream.iterate​(int seed, IntPredicate hasNext, IntUnaryOperator next);

  • seed - องค์ประกอบเริ่มต้น
  • hasNext - เพรดิเคตเพื่อใช้กับองค์ประกอบเพื่อกำหนดว่าเมื่อใดสตรีมจะต้องยุติ
  • next - ฟังก์ชั่นที่จะใช้กับองค์ประกอบก่อนหน้าเพื่อสร้างองค์ประกอบใหม่

1

สำหรับการอ้างอิงฉันดูปัญหาเดียวกันฉันต้องการเข้าร่วมค่าสตริงขององค์ประกอบสตรีมในลำดับย้อนกลับ

itemList = {last, middle, first} => ก่อน, middle, last

ฉันเริ่มใช้คอลเล็กชั่นระดับกลางด้วยcollectingAndThenจากcomonadหรือArrayDequeนักสะสมของStuart Marksแม้ว่าฉันจะไม่พอใจกับการรวบรวมระดับกลางและสตรีมอีกครั้ง

itemList.stream()
        .map(TheObject::toString)
        .collect(Collectors.collectingAndThen(Collectors.toList(),
                                              strings -> {
                                                      Collections.reverse(strings);
                                                      return strings;
                                              }))
        .stream()
        .collect(Collector.joining());

ดังนั้นผมจึงซ้ำมากกว่าคำตอบ Stuart เครื่องหมายที่ใช้Collector.ofในโรงงานที่มีที่น่าสนใจหมัดเด็ดแลมบ์ดา

itemList.stream()
        .collect(Collector.of(StringBuilder::new,
                             (sb, o) -> sb.insert(0, o),
                             (r1, r2) -> { r1.insert(0, r2); return r1; },
                             StringBuilder::toString));

เนื่องจากในกรณีนี้สตรีมไม่ขนานกัน combiner จึงไม่ค่อยเกี่ยวข้องเท่าไหร่ฉันจึงใช้ insertต่อไปเพื่อประโยชน์ของความสอดคล้องของรหัส แต่มันไม่สำคัญว่ามันจะขึ้นอยู่กับการสร้างสตริงตัวแรก

ฉันดู StringJoiner แต่มันไม่มีinsertวิธี


1

ตอบคำถามเฉพาะของการย้อนกลับด้วย IntStream ด้านล่างใช้ได้สำหรับฉัน:

IntStream.range(0, 10)
  .map(x -> x * -1)
  .sorted()
  .map(Math::abs)
  .forEach(System.out::println);

1

ArrayDequeเร็วกว่าในสแต็กกว่า Stack หรือ LinkedList "push ()" แทรกองค์ประกอบที่ด้านหน้า Deque

 protected <T> Stream<T> reverse(Stream<T> stream) {
    ArrayDeque<T> stack = new ArrayDeque<>();
    stream.forEach(stack::push);
    return stack.stream();
}

1

การย้อนกลับสตริงหรืออาร์เรย์ใด ๆ

(Stream.of("abcdefghijklm 1234567".split("")).collect(Collectors.collectingAndThen(Collectors.toList(),list -> {Collections.reverse(list);return list;}))).stream().forEach(System.out::println);

แยกสามารถแก้ไขได้ตามตัวคั่นหรือพื้นที่


1

ทางออกที่ง่ายที่สุดคือการใช้List::listIteratorและStream::generate

List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
ListIterator<Integer> listIterator = list.listIterator(list.size());

Stream.generate(listIterator::previous)
      .limit(list.size())
      .forEach(System.out::println);

มันเป็นการเพิ่มมูลค่าที่Stream.generate()สร้างในสตรีมที่ไม่มีที่สิ้นสุดดังนั้นการโทรถึงlimit()สำคัญมากที่นี่
andrebrait

0

นี่คือวิธีที่ฉันทำ

ฉันไม่ชอบความคิดในการสร้างคอลเลกชันใหม่และย้อนกลับไปทำซ้ำ

ความคิดแผนที่ IntStream # ค่อนข้างเรียบร้อย แต่ฉันชอบวิธี IntStream # iterate เพราะฉันคิดว่าความคิดในการนับถอยหลังถึง Zero ที่แสดงออกมาดีกว่าด้วยวิธีการวนซ้ำและเข้าใจง่ายกว่าในแง่ของการเดินแถวจากด้านหน้าไปด้านหลัง

import static java.lang.Math.max;

private static final double EXACT_MATCH = 0d;

public static IntStream reverseStream(final int[] array) {
    return countdownFrom(array.length - 1).map(index -> array[index]);
}

public static DoubleStream reverseStream(final double[] array) {
    return countdownFrom(array.length - 1).mapToDouble(index -> array[index]);
}

public static <T> Stream<T> reverseStream(final T[] array) {
    return countdownFrom(array.length - 1).mapToObj(index -> array[index]);
}

public static IntStream countdownFrom(final int top) {
    return IntStream.iterate(top, t -> t - 1).limit(max(0, (long) top + 1));
}

นี่คือการทดสอบบางอย่างเพื่อพิสูจน์ว่าใช้งานได้:

import static java.lang.Integer.MAX_VALUE;
import static org.junit.Assert.*;

@Test
public void testReverseStream_emptyArrayCreatesEmptyStream() {
    Assert.assertEquals(0, reverseStream(new double[0]).count());
}

@Test
public void testReverseStream_singleElementCreatesSingleElementStream() {
    Assert.assertEquals(1, reverseStream(new double[1]).count());
    final double[] singleElementArray = new double[] { 123.4 };
    assertArrayEquals(singleElementArray, reverseStream(singleElementArray).toArray(), EXACT_MATCH);
}

@Test
public void testReverseStream_multipleElementsAreStreamedInReversedOrder() {
    final double[] arr = new double[] { 1d, 2d, 3d };
    final double[] revArr = new double[] { 3d, 2d, 1d };
    Assert.assertEquals(arr.length, reverseStream(arr).count());
    Assert.assertArrayEquals(revArr, reverseStream(arr).toArray(), EXACT_MATCH);
}

@Test
public void testCountdownFrom_returnsAllElementsFromTopToZeroInReverseOrder() {
    assertArrayEquals(new int[] { 4, 3, 2, 1, 0 }, countdownFrom(4).toArray());
}

@Test
public void testCountdownFrom_countingDownStartingWithZeroOutputsTheNumberZero() {
    assertArrayEquals(new int[] { 0 }, countdownFrom(0).toArray());
}

@Test
public void testCountdownFrom_doesNotChokeOnIntegerMaxValue() {
    assertEquals(true, countdownFrom(MAX_VALUE).anyMatch(x -> x == MAX_VALUE));
}

@Test
public void testCountdownFrom_givesZeroLengthCountForNegativeValues() {
    assertArrayEquals(new int[0], countdownFrom(-1).toArray());
    assertArrayEquals(new int[0], countdownFrom(-4).toArray());
}

0

ทั้งหมดนี้ฉันไม่เห็นคำตอบที่ฉันจะไปก่อน

นี่ไม่ใช่คำตอบที่ตรงกับคำถาม แต่เป็นวิธีแก้ปัญหาที่เป็นไปได้

เพียงสร้างรายการย้อนหลังในสถานที่แรก หากสามารถทำได้ให้ใช้ LinkedList แทน ArrayList และเมื่อคุณเพิ่มรายการให้ใช้ "Push" แทนการเพิ่ม รายการจะถูกสร้างขึ้นในลำดับย้อนกลับและจากนั้นจะสตรีมอย่างถูกต้องโดยไม่มีการจัดการใด ๆ

กรณีนี้จะไม่เหมาะกับกรณีที่คุณกำลังเผชิญกับอาร์เรย์ดั้งเดิมหรือรายการที่ใช้อยู่แล้วในหลายวิธี แต่ทำงานได้ดีในหลายกรณี


0

วิธีนี้ใช้ได้กับทุกกระแสข้อมูลและเป็นไปตามมาตรฐาน Java 8:

Stream<Integer> myStream = Stream.of(1, 2, 3, 4, 5);
myStream.reduce(Stream.empty(),
        (Stream<Integer> a, Integer b) -> Stream.concat(Stream.of(b), a),
        (a, b) -> Stream.concat(b, a))
        .forEach(System.out::println);

-1

วิธีที่ธรรมดาที่สุดและง่ายที่สุดในการกลับรายการคือ:

public static <T> void reverseHelper(List<T> li){

 li.stream()
.sorted((x,y)-> -1)
.collect(Collectors.toList())
.forEach(System.out::println);

    }

4
Comparatorคุณละเมิดสัญญาของ ด้วยเหตุนี้จึงไม่มีใครรับรองได้ว่า "การหลอกลวง" นี้จะใช้ได้ในทุก ๆ เวอร์ชั่น Java ในอนาคตด้วยอัลกอริทึมการเรียงลำดับใด ๆ เคล็ดลับเดียวกันนี้ใช้ไม่ได้กับสตรีมแบบขนานเช่นอัลกอริทึมการเรียงลำดับแบบขนานใช้Comparatorในวิธีที่ต่างกัน สำหรับการเรียงลำดับต่อเนื่องมันทำงานได้อย่างหมดจดโดยบังเอิญ ฉันจะไม่แนะนำให้ใครใช้โซลูชันนี้
Tagir Valeev

1
นอกจากนี้มันจะไม่ทำงานเมื่อคุณตั้งค่าSystem.setProperty("java.util.Arrays.useLegacyMergeSort", "true");
Tagir Valeev

ฉันคิดว่ามันเป็นเรื่องเกี่ยวกับการพิมพ์เพียงแค่สิ่งที่อยู่ในลำดับที่กลับไม่อยู่ในลำดับที่เรียงลำดับและการทำงานกับกระแสขนานเกินไป (เช่นหลั่น.) public static <T> void reverseHelper(List<T> li){ li.parallelStream() .sorted((x,y)->-1) .collect(Collectors.toList()) .forEach(System.out::println); }
parmeshwor11

1
ลองreverseHelper(IntStream.range(0, 8193).boxed().collect(Collectors.toList()))(ผลลัพธ์อาจขึ้นอยู่กับจำนวนของคอร์)
Tagir Valeev

-1

Java 8 วิธีทำ:

    List<Integer> list = Arrays.asList(1,2,3,4);
    Comparator<Integer> comparator = Integer::compare;
    list.stream().sorted(comparator.reversed()).forEach(System.out::println);

4
นี่คือการเรียงลำดับในลำดับย้อนหลังไม่คืนค่ารายการ
Jochen Bedersdorfer
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.