Thứ tự ngược dòng Java 8


153

Câu hỏi chung: Cách thích hợp để đảo ngược luồng? Giả sử rằng chúng ta không biết loại phần tử đó bao gồm những gì, cách chung để đảo ngược bất kỳ luồng nào?

Câu hỏi cụ thể:

IntStreamcung cấp phương pháp phạm vi để tạo Số nguyên trong phạm vi cụ thể IntStream.range(-range, 0), bây giờ tôi muốn đảo ngược phạm vi chuyển đổi từ 0 sang tiêu cực sẽ không hoạt động, tôi cũng không thể sử dụngInteger::compare

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

với IntStreamtôi sẽ gặp lỗi trình biên dịch này

Lỗi: (191, 0) ajc: Phương thức sorted()trong loại IntStreamkhông áp dụng cho các đối số ( Integer::compare)

tôi đang thiếu gì ở đây


1
An IntStreamkhông có .sorted(Comparator)phương pháp; bạn phải trải qua Stream<Integer>lần đầu tiên và đảo ngược ở đó trước khi đạt được mộtIntStream
fge

Ok, tôi đã tìm thấy nó, bạn cần sử dụng .boxed () và sau đó .sort ()
vach

4
Để tạo một IntStream.range(0, n)thứ tự ngược lại, làm một cái gì đó như map(i -> n - i - 1). Không cần phải làm quyền anh và phân loại.
Stuart Marks

1
Câu hỏi của bạn và câu hỏi cụ thể của bạn đọc giống như hai câu hỏi hoàn chỉnh khác nhau đối với tôi. Nói chung nói về việc đảo ngược dòng , trong khi cụ thể nói về thứ tự số theo thứ tự giảm dần. Nếu luồng tạo ra các số theo cách không có thứ tự như thế nào 1, 3, 2, kết quả mong đợi của bạn là gì? Bạn có muốn luồng đảo ngược như 2, 3, 1hoặc luồng được sắp xếp như thế 3, 2, 1nào không?
chiccodoro

8
Bạn không thể đảo ngược một luồng nói chung - ví dụ: một luồng có thể không xác định.
assylias

Câu trả lời:


78

Đối với câu hỏi cụ thể về việc tạo đảo ngược IntStream, hãy thử một cái gì đó như thế này:

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

Điều này tránh đấm bốc và phân loại.

Đối với câu hỏi chung về cách đảo ngược bất kỳ loại nào, tôi không biết có cách nào "phù hợp". Có một vài cách tôi có thể nghĩ ra. Cả hai cuối cùng lưu trữ các yếu tố dòng. Tôi không biết cách đảo ngược luồng mà không lưu trữ các phần tử.

Cách đầu tiên này lưu trữ các phần tử thành một mảng và đọc chúng ra một luồng theo thứ tự ngược lại. Lưu ý rằng vì chúng tôi không biết loại thời gian chạy của các thành phần luồng, chúng tôi không thể nhập đúng mảng, yêu cầu bỏ chọn không được kiểm tra.

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

Một kỹ thuật khác sử dụng các bộ sưu tập để tích lũy các mục vào một danh sách đảo ngược. Điều này không có nhiều phần chèn vào phía trước các ArrayListđối tượng, vì vậy có rất nhiều sự sao chép đang diễn ra.

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

Có lẽ có thể viết một trình thu thập đảo ngược hiệu quả hơn bằng cách sử dụng một số loại cấu trúc dữ liệu tùy chỉnh.

CẬP NHẬT 2016-01-29

Vì câu hỏi này đã nhận được một chút chú ý gần đây, tôi cho rằng tôi nên cập nhật câu trả lời của mình để giải quyết vấn đề bằng cách chèn ở phía trước ArrayList. Điều này sẽ không hiệu quả khủng khiếp với một số lượng lớn các yếu tố, yêu cầu sao chép O (N ^ 2).

Tốt nhất là sử dụng ArrayDequethay thế, hỗ trợ hiệu quả việc chèn ở phía trước. Một nếp nhăn nhỏ là chúng ta không thể sử dụng hình thức ba đối số Stream.collect(); nó yêu cầu nội dung của đối số thứ hai được hợp nhất vào đối số thứ nhất và không có thao tác hàng loạt "bổ sung tất cả ở phía trước" Deque. Thay vào đó, chúng tôi sử dụng addAll()để nối các nội dung của đối số thứ nhất vào cuối giây thứ hai và sau đó chúng tôi trả lại nội dung thứ hai. Điều này đòi hỏi phải sử dụng Collector.of()phương pháp nhà máy.

Mã hoàn chỉnh là đây:

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

Kết quả là Dequethay vì một List, nhưng điều đó không phải là vấn đề, vì nó có thể dễ dàng được lặp lại hoặc truyền phát theo thứ tự đảo ngược.


15
Ngoài ra:IntStream.iterate(to-1, i->i-1).limit(to-from)
Holger

2
@Holger Thật không may, giải pháp đó không xử lý tràn chính xác.
Brandon

2
@Brandon Mitern: thực sự, bạn sẽ phải sử dụng .limit(endExcl-(long)startIncl)thay thế, nhưng đối với các luồng lớn như vậy, dù sao thì nó cũng rất nản lòng vì nó kém hiệu quả hơn nhiều so với rangegiải pháp dựa trên. Vào thời điểm tôi viết bình luận, tôi không nhận thức được sự khác biệt về hiệu quả.
Holger

48

Giải pháp thanh lịch

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
Nó thanh lịch nhưng không hoạt động đầy đủ vì có vẻ như các yếu tố của danh sách được yêu cầu phải Comparable...
Krzysztof Wolny

26
Giả sử chúng ta muốn các phần tử được sắp xếp theo thứ tự ngược lại. Câu hỏi là về việc đảo ngược thứ tự của một luồng.
Dillon Ryan Redding

37

Nhiều giải pháp ở đây sắp xếp hoặc đảo ngược IntStream, nhưng không cần thiết phải lưu trữ trung gian. Giải pháp của Stuart Marks là con đường để đi:

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

Nó cũng xử lý chính xác tràn, vượt qua bài kiểm tra này:

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

tuyệt vời và đơn giản. Đây có phải là từ một số mã nguồn mở? (điều Estreams) hoặc một số đoạn mã của bạn?
vach

Cảm ơn! Thật không may, tôi không có ý định rò rỉ Estreamstên này (tôi sẽ xóa nó khỏi bài viết). Đây là một trong những lớp tiện ích nội bộ của công ty chúng tôi, chúng tôi sử dụng để bổ sung java.util.stream.Streamcho các staticphương thức của mình.
Brandon

3
Được rồi, Sốt đơn giản và đơn giản, có bất kỳ trường hợp nào mà giải pháp này xử lý, mà giải pháp thậm chí đơn giản hơn của Stuart Marks đã không xử lý hơn một năm rưỡi trước?
Holger

Tôi chỉ thử nghiệm giải pháp của mình với phương pháp thử nghiệm của tôi ở trên; nó đi. Tôi không cần thiết phải tránh tràn ra thay vì ôm lấy nó như anh ấy đã làm. Tôi đồng ý rằng anh ấy tốt hơn. Tôi sẽ chỉnh sửa của tôi để phản ánh điều đó.
Brandon

1
@vach, bạn có thể sử dụng bằng cách StreamExchỉ định bước:IntStreamEx.rangeClosed(from-1, to, -1)
Tagir Valeev

34

Câu hỏi chung:

Luồng không lưu trữ bất kỳ yếu tố nào.

Vì vậy, các phần tử lặp theo thứ tự ngược lại là không thể mà không lưu trữ các phần tử trong một số bộ sưu tập trung gian.

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

Cập nhật: Đã thay đổi LinkedList thành ArrayDeque (tốt hơn) xem tại đây để biết chi tiết

Bản in:

3

20

2

1

Nhân tiện, sử dụng sortphương thức không đúng khi sắp xếp, KHÔNG đảo ngược (giả sử luồng có thể có các phần tử không có thứ tự)

Câu hỏi cụ thể:

Tôi thấy điều này đơn giản, dễ dàng và trực quan hơn (Sao chép @Holger bình luận )

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

3
Một số hoạt động luồng, chẳng hạn như sorteddistinctthực sự lưu trữ một kết quả trung gian. Xem tài liệu API gói để biết một số thông tin về điều đó.
Lii

@Lii Tôi thấy No storagetrong cùng một trang. Ngay cả cửa hàng, chúng tôi cũng không thể truy cập vào bộ lưu trữ đó ( No storagetôi cũng đoán vậy)
Venkata Raju

Câu trả lời tốt. Nhưng vì không gian thêm được sử dụng, nên nhiều lập trình viên không nên sử dụng phương pháp của bạn trên bộ sưu tập rất lớn.
Manu Manathath

Tôi thích sự đơn giản của giải pháp từ góc độ dễ hiểu và tận dụng một phương thức hiện có trên cấu trúc dữ liệu hiện có ... giải pháp trước đây khi triển khai bản đồ khó hiểu hơn nhưng chắc chắn Manu đã đúng, đối với các bộ sưu tập lớn tôi sẽ không sử dụng trực quan, và sẽ chọn cho bản đồ ở trên.
Beezer

Đây là một trong số ít câu trả lời đúng ở đây. Hầu hết những cái khác không thực sự đảo ngược luồng, họ cố gắng tránh làm điều đó bằng cách nào đó (chỉ hoạt động trong các tình huống đặc biệt mà theo đó bạn thường không cần phải đảo ngược ngay từ đầu). Nếu bạn cố gắng đảo ngược một luồng không phù hợp với bộ nhớ thì bạn vẫn đang làm sai. Kết xuất nó vào DB và nhận luồng đảo ngược bằng SQL thông thường.
Khối

19

không có lib bên ngoài ...

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

Nếu thực hiện Comparable<T>(ví dụ. Integer, String, Date), Bạn có thể làm điều đó bằng Comparator.reverseOrder().

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

6
Điều này không đảo ngược dòng. Nó sắp xếp các luồng theo thứ tự ngược lại. Vì vậy, nếu bạn có Stream.of(1,3,2)kết quả sẽ Stream.of(3,2,1)KHÔNGStream.of(2,3,1)
wilmol

12

Bạn có thể xác định trình thu thập của riêng mình để thu thập các phần tử theo thứ tự ngược lại:

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

Và sử dụng nó như:

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

Tôi sử dụng một ArrayList theo thứ tự để chèn một cách hiệu quả việc thu thập các mục (ở cuối danh sách) và Guava Lists.reverse để đưa ra một cái nhìn đảo ngược của danh sách một cách hiệu quả mà không tạo ra một bản sao khác của nó.

Dưới đây là một số trường hợp thử nghiệm cho trình thu thập tùy chỉnh:

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

Streamops - React StreamUtils có phương pháp Stream ngược ( javadoc ).

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

Nó hoạt động bằng cách thu thập vào một ArrayList và sau đó sử dụng lớp ListIterator có thể lặp theo một trong hai hướng, để lặp lại qua danh sách.

Nếu bạn đã có một Danh sách, nó sẽ hiệu quả hơn

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

1
Nice không biết về dự án này
vach

1
cyclops bây giờ cũng đi kèm với Spliterators để đảo ngược luồng hiệu quả (hiện tại cho Phạm vi, mảng và Danh sách). Tạo cyclops SequenceM Stream mở rộng với SequenceM.of, SequenceM.range hoặc SequenceM.fromList sẽ tự động tận dụng các bộ chia đảo ngược hiệu quả.
John McClean

6

Tôi sẽ đề nghị sử dụng jOOλ , đây là một thư viện tuyệt vời bổ sung nhiều chức năng hữu ích cho các luồng Java và lambdas.

Sau đó, bạn có thể làm như sau:

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

Đơn giản như vậy. Đây là một thư viện khá nhẹ và đáng để thêm vào bất kỳ dự án Java 8 nào.


5

Đây là giải pháp tôi đã đưa ra:

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

sau đó sử dụng các bộ so sánh đó:

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

2
Đây chỉ là một câu trả lời cho "câu hỏi cụ thể" của bạn chứ không phải cho "câu hỏi chung" của bạn.
chiccodoro

9
Collections.reverseOrder()tồn tại kể từ Java 1.2 và làm việc với Integer...
Holger

4

Làm thế nào về phương pháp tiện ích này?

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

Có vẻ để làm việc với tất cả các trường hợp mà không trùng lặp.


Tôi thích giải pháp này rất nhiều. Hầu hết các câu trả lời được chia thành hai loại: (1) Đảo ngược bộ sưu tập và .stream () nó, (2) Khiếu nại với người thu thập tùy chỉnh. Cả hai đều hoàn toàn không cần thiết. Mặt khác, nó đã làm chứng về một số vấn đề biểu hiện ngôn ngữ nghiêm trọng trong chính JDK 8. Và câu trả lời của bạn đã chứng minh điều ngược lại :)
vitrums

4
List newStream = list.stream().sorted(Collections.reverseOrder()).collect(Collectors.toList());
        newStream.forEach(System.out::println);

3

Cách đơn giản nhất (thu thập đơn giản - hỗ trợ các luồng song song):

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

Cách nâng cao (hỗ trợ các luồng song song theo cách liên tục):

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

Lưu ý rằng bạn có thể nhanh chóng mở rộng sang loại luồng khác (IntStream, ...).

Kiểm tra:

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

Các kết quả:

Six
Five
Four
Three
Two
One

Ghi chú thêm: Các simplest waynó không phải là quá hữu ích khi được sử dụng với các hoạt động dòng khác (thu thập tham gia phá vỡ song song). Các advance waykhông có vấn đề đó, và nó cũng giữ những đặc điểm ban đầu của dòng, ví dụ SORTED, và vì vậy, nó là con đường để đi đến việc sử dụng với các hoạt động dòng khác sau khi điều ngược lại.


2

Người ta có thể viết một bộ sưu tập thu thập các phần tử theo thứ tự đảo ngược:

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

Và sử dụng nó như thế này:

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

Câu trả lời gốc (có lỗi - nó không hoạt động chính xác cho các luồng song song):

Một phương pháp đảo ngược dòng mục đích chung có thể trông giống như:

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

2

Không hoàn toàn là Java8 nhưng nếu bạn sử dụng kết hợp phương thức Lists.reverse () của ổi, bạn có thể dễ dàng đạt được điều này:

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

2

Đối với câu hỏi cụ thể về việc tạo ra một đảo ngược IntStream :

bắt đầu từ Java 9, bạn có thể sử dụng phiên bản ba đối số của 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

Ở đâu:

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

  • seed - yếu tố ban đầu;
  • hasNext - một vị từ để áp dụng cho các phần tử để xác định khi nào luồng phải chấm dứt;
  • next - một chức năng được áp dụng cho phần tử trước để tạo ra phần tử mới.

1

Để tham khảo tôi đã xem xét cùng một vấn đề, tôi muốn tham gia giá trị chuỗi của các phần tử luồng theo thứ tự ngược lại.

itemList = {cuối cùng, giữa, đầu tiên} => đầu tiên, giữa, cuối cùng

Tôi bắt đầu sử dụng một bộ sưu tập trung gian với collectingAndThentừ comonad hoặc ArrayDequenhà sưu tập của Stuart Marks , mặc dù tôi không hài lòng với bộ sưu tập trung gian và phát lại

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

Vì vậy, tôi đã lặp lại câu trả lời của Stuart Marks đang sử dụng Collector.ofnhà máy, có lambda hoàn thiện thú vị .

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

Vì trong trường hợp này, luồng không song song, bộ kết hợp không liên quan nhiều, tôi đang sử dụng insert dù sao vì mục đích thống nhất mã nhưng điều đó không quan trọng vì nó phụ thuộc vào việc xây dựng chuỗi nào được xây dựng trước.

Tôi đã xem StringJoiner, tuy nhiên nó không có insertphương thức.


1

Trả lời câu hỏi cụ thể về việc đảo ngược với IntStream, bên dưới hoạt động với tôi:

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

1

ArrayDequenhanh hơn trong ngăn xếp so với Stack hoặc LinkedList. "push ()" chèn các phần tử ở phía trước Deque

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

1

Đảo ngược chuỗi hoặc bất kỳ mảng nào

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

phân chia có thể được sửa đổi dựa trên dấu phân cách hoặc dấu cách


1

giải pháp đơn giản nhất là sử dụng List::listIteratorStream::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);

Thật đáng để thêm vào Stream.generate()tạo ra luồng vô hạn, vì vậy cuộc gọi đến limit()rất quan trọng ở đây.
andreb Eo

0

Đây là cách tôi làm điều đó.

Tôi không thích ý tưởng tạo ra một bộ sưu tập mới và lặp lại nó.

Ý tưởng bản đồ IntStream # khá gọn gàng, nhưng tôi thích phương pháp IntStream # iterate hơn, vì tôi nghĩ ý tưởng đếm ngược đến Zero được thể hiện tốt hơn với phương pháp lặp và dễ hiểu hơn khi đi theo mảng từ trước ra trước.

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

Dưới đây là một số thử nghiệm để chứng minh rằng nó hoạt động:

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

Trong tất cả điều này tôi không thấy câu trả lời tôi sẽ đi đến đầu tiên.

Đây không chính xác là một câu trả lời trực tiếp cho câu hỏi, nhưng nó là một giải pháp tiềm năng cho vấn đề.

Chỉ cần xây dựng danh sách ngược ở vị trí đầu tiên. Nếu bạn có thể, hãy sử dụng LinkedList thay vì ArrayList và khi bạn thêm các mục, hãy sử dụng "Push" thay vì thêm. Danh sách sẽ được xây dựng theo thứ tự ngược lại và sau đó sẽ truyền phát chính xác mà không cần bất kỳ thao tác nào.

Điều này sẽ không phù hợp với các trường hợp bạn đang xử lý các mảng hoặc danh sách nguyên thủy đã được sử dụng theo nhiều cách khác nhau nhưng hoạt động tốt trong một số trường hợp đáng ngạc nhiên.


0

Phương thức này hoạt động với bất kỳ Luồng nào và tuân thủ 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

Cách chung nhất và dễ nhất để đảo ngược danh sách sẽ là:

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

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

    }

4
Bạn vi phạm hợp đồng Comparator. Kết quả là không ai có thể đảm bảo với bạn rằng "mẹo" này sẽ hoạt động trong mọi phiên bản Java trong tương lai với bất kỳ thuật toán sắp xếp nào. Ví dụ, thủ thuật tương tự không hoạt động đối với luồng song song, vì thuật toán sắp xếp song song sử dụng Comparatortheo cách khác nhau. Đối với sắp xếp tuần tự, nó hoạt động hoàn toàn tình cờ. Tôi sẽ không đề nghị bất cứ ai sử dụng giải pháp này.
Tagir Valeev

1
Ngoài ra, nó không hoạt động khi bạn đặtSystem.setProperty("java.util.Arrays.useLegacyMergeSort", "true");
Tagir Valeev

Tôi nghĩ rằng đó là về việc chỉ in những thứ theo thứ tự ngược lại không theo thứ tự được sắp xếp (nghĩa là theo thứ tự giảm dần) và nó cũng hoạt động với luồng song song: public static <T> void reverseHelper(List<T> li){ li.parallelStream() .sorted((x,y)->-1) .collect(Collectors.toList()) .forEach(System.out::println); }
parmeshwor11

1
Hãy thử reverseHelper(IntStream.range(0, 8193).boxed().collect(Collectors.toList()))(kết quả có thể phụ thuộc vào số lượng lõi).
Tagir Valeev

-1

Cách Java 8 để làm điều này:

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

4
Đây là sắp xếp theo thứ tự ngược lại, không hoàn nguyên một danh sách.
Jochen Bedersdorfer
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.