سؤال

سؤال عام:ما هي الطريقة الصحيحة لعكس تيار?على افتراض أننا لا نعرف ما هو نوع من العناصر التي تيار يتكون من, ما هي الطريقة العامة لعكس أي تيار?

سؤال محدد:

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) آي جي سي:الطريقة sorted() في النوع IntStream لا ينطبق على الحجج (Integer::compare)

ما أنا في عداد المفقودين هنا?

هل كانت مفيدة؟

المحلول

بالنسبة للسؤال المحدد المتمثل في توليد عكس 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.سيكون هذا غير فعال بشكل فظيع مع عدد كبير من العناصر ، مما يتطلب نسخا(ن^2).

من الأفضل استخدام ملف ArrayDeque بدلا من ذلك ، الذي يدعم بكفاءة الإدراج في الجبهة.تجعد صغير هو أننا لا نستطيع استخدام شكل ثلاثة أرغ من Stream.collect();فإنه يتطلب محتويات أرغ الثاني يتم دمجها في أرغ الأول ، وليس هناك" إضافة الكل في الجبهة " عملية السائبة على 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 بدلا من List, ، ولكن لا ينبغي أن تكون هذه مشكلة كبيرة ، حيث يمكن تكرارها أو بثها بسهولة بالترتيب المعكوس الآن.

نصائح أخرى

حل أنيق

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

العديد من الحلول هنا فرز أو عكس IntStream, ، لكن هذا يتطلب دون داع تخزينا وسيطا. حل ستيوارت ماركس هو الطريق للذهاب:

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

سؤال عام:

تيار لا تخزن أي عناصر.

لذا فإن تكرار العناصر بالترتيب العكسي غير ممكن بدون تخزين العناصر في بعض المجموعات الوسيطة.

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

تحديث:تغيير لينكدليست إلى أريديك (أفضل) انظر هنا للحصول على التفاصيل

المطبوعات:

3

20

2

1

بالمناسبة ، باستخدام sort الطريقة غير صحيحة لأنها تفرز ، ولا تنعكس (بافتراض أن الدفق قد يحتوي على عناصر غير مرتبة)

سؤال محدد:

لقد وجدت هذا بسيطة وأسهل وبديهية (نسخ @ هولجر التعليق)

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

بدون ليب خارجي...

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

}

إذا نفذت 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);

يمكنك تحديد المجمع الخاص بك الذي يجمع العناصر بترتيب عكسي:

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 -> ...)

يمكنني استخدام أريليست في ترتيب إلى الأمام لإدراج بكفاءة جمع العناصر (في نهاية القائمة) ، وقوائم الجوافة.عكس لإعطاء بكفاءة وجهة نظر عكس القائمة دون إجراء نسخة أخرى منه.

فيما يلي بعض حالات الاختبار للمجمع المخصص:

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

العملاق-رد فعل ستريموتيلز لديه طريقة تيار عكسي (جافادوك).

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

وهو يعمل عن طريق جمع إلى أريليست ومن ثم الاستفادة من فئة ليستيتيراتور التي يمكن أن تتكرر في أي من الاتجاهين ، لتكرار الوراء على القائمة.

إذا كان لديك بالفعل قائمة ، فستكون أكثر كفاءة

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

وهنا الحل لقد جئت مع:

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...

أود أن أقترح استخدام جوoo, ، إنها مكتبة رائعة تضيف الكثير من الوظائف المفيدة إلى جافا 8 تيارات و لامبداس.

يمكنك بعد ذلك القيام بما يلي:

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

بهذه البساطة.انها مكتبة خفيفة الوزن جدا ، وتستحق إضافة إلى أي مشروع جافا 8.

أبسط طريقة (جمع بسيط-يدعم تيارات متوازية):

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

ملاحظة يمكنك يمتد بسرعة إلى نوع آخر من تيارات (إنتستريم,...).

اختبار:

// 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, ، وهكذا ، انها وسيلة للذهاب لاستخدامها مع عمليات تيار أخرى بعد العكس.

ماذا عن هذه الطريقة فائدة?

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

يبدو أنه يعمل مع جميع الحالات دون ازدواجية.

يمكن للمرء أن يكتب جامع يجمع العناصر بترتيب معكوس:

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

فيما يتعلق بالمسألة المحددة المتمثلة في توليد عكس IntStream:

بدءا من جافا 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 - وظيفة ليتم تطبيقها على العنصر السابق لإنتاج عنصر جديد.

كمرجع كنت أبحث في نفس المشكلة ، أردت الانضمام إلى قيمة سلسلة عناصر الدفق بالترتيب العكسي.

قائمة العناصر = { الأخير ، الأوسط ، الأول} = > الأول ، الأوسط ، الأخير

بدأت في استخدام مجموعة وسيطة مع collectingAndThen من عند كوموناد أو ال ArrayDeque جامع ستيوارت ماركس, ، على الرغم من أنني لم أكن سعيدا بالمجموعة الوسيطة ، والبث مرة أخرى

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

لذلك كررت أكثر من ستيوارت ماركس الإجابة التي كانت تستخدم 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));

نظرا لأن الدفق في هذه الحالة ليس متوازيا، فإن المجمع ليس ذا صلة كثيرا ، فأنا أستخدمه insert على أي حال من أجل اتساق التعليمات البرمجية ولكن لا يهم لأنها تعتمد على أي سترينغبويلدر بنيت أولا.

نظرت إلى سترينججوينر ، ولكن ليس لديها insert الطريقة.

الإجابة على سؤال محدد من عكس مع إنتستريم ، أدناه عملت بالنسبة لي:

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

ليس جافا8 بحتة ولكن إذا كنت تستخدم قوائم الجوافة.طريقة عكسية () بالتزامن ، يمكنك تحقيق ذلك بسهولة:

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

ArrayDeque أسرع في المكدس من مكدس أو لينكدليست."دفع ()" إدراج عناصر في الجزء الأمامي من ديك

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

عكس سلسلة أو أي صفيف

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

يمكن تعديل الانقسام بناء على المحدد أو المساحة

أبسط حل هو استخدام 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);

هذه هي الطريقة التي أفعل ذلك.

أنا لا أحب فكرة إنشاء مجموعة جديدة وعكس تكرار ذلك.

فكرة إنتستريم # خريطة هو أنيق جدا ، ولكن أنا أفضل طريقة إنتستريم # تكرار ، لأنني أعتقد أن فكرة العد التنازلي إلى الصفر أعرب أفضل مع طريقة تكرار وأسهل للفهم من حيث المشي مجموعة من الخلف إلى الأمام.

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

في كل هذا لا أرى الإجابة التي سأذهب إليها أولا.

هذه ليست بالضبط إجابة مباشرة على السؤال ، لكنها حل محتمل للمشكلة.

مجرد بناء القائمة إلى الوراء في المقام الأول.إذا كنت تستطيع ، استخدم لينكدليست بدلا من أريليست وعند إضافة عناصر استخدام "دفع" بدلا من إضافة.سيتم بناء القائمة في ترتيب عكسي وسوف ثم تيار بشكل صحيح دون أي تلاعب.

لن يتناسب هذا مع الحالات التي تتعامل فيها مع صفائف أو قوائم بدائية مستخدمة بالفعل بطرق مختلفة ولكنها تعمل بشكل جيد في عدد مذهل من الحالات.

قائمة تيار جديد = قائمة.تيار ().مرتبة (مجموعات.ترتيب عكسي ()).جمع (جامعي.توليست());نيوستريم.فوريش (النظام.خارج:: برينتلن);

الطريقة الأكثر عمومية والأسهل لعكس القائمة ستكون :

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

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

    }

جافا 8 طريقة للقيام بذلك:

    List<Integer> list = Arrays.asList(1,2,3,4);
    Comparator<Integer> comparator = Integer::compare;
    list.stream().sorted(comparator.reversed()).forEach(System.out::println);
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top