Có cách nào ngắn gọn để lặp lại một luồng với các chỉ mục trong Java 8 không?


382

Có cách nào ngắn gọn để lặp lại qua luồng trong khi có quyền truy cập vào chỉ mục trong luồng không?

String[] names = {"Sam","Pamela", "Dave", "Pascal", "Erik"};

List<String> nameList;
Stream<Integer> indices = intRange(1, names.length).boxed();
nameList = zip(indices, stream(names), SimpleEntry::new)
        .filter(e -> e.getValue().length() <= e.getKey())
        .map(Entry::getValue)
        .collect(toList());

có vẻ khá thất vọng so với ví dụ LINQ được đưa ra ở đó

string[] names = { "Sam", "Pamela", "Dave", "Pascal", "Erik" };
var nameList = names.Where((c, index) => c.Length <= index + 1).ToList();

Có cách nào súc tích hơn không?

Hơn nữa, có vẻ như zip đã bị di chuyển hoặc bị xóa ...


2
intRange()gì Cho đến nay, chúng ta không bắt gặp phương thức này trong Java 8.
Rohit Jain

@RohitJain Có lẽ là một IntStream.rangeClosed(x, y).
assylias

2
Như một bình luận phụ, thử thách 4 có vẻ tốt hơn (IMO) vớiList<String> allCities = map.values().stream().flatMap(list -> list.stream()).collect(Collectors.toList());
assylias

3
Có, zipđã bị xóa, cùng với các luồng hai giá trị thử nghiệm được gọi khác nhau BiStreamhoặc MapStream. Vấn đề chính là để thực hiện điều này một cách hiệu quả, Java thực sự cần một kiểu gõ (hoặc tuple) có cấu trúc. Thiếu một cái, thật dễ dàng để tạo một lớp Cặp hoặc Tuple chung - nó đã được thực hiện nhiều lần - nhưng tất cả chúng đều xóa cùng một loại.
Stuart Marks

3
Ồ, một vấn đề khác với lớp Pair hoặc Tuple chung là nó yêu cầu tất cả các nguyên hàm phải được đóng hộp.
Stuart Marks

Câu trả lời:


435

Cách sạch nhất là bắt đầu từ một luồng các chỉ số:

String[] names = {"Sam", "Pamela", "Dave", "Pascal", "Erik"};
IntStream.range(0, names.length)
         .filter(i -> names[i].length() <= i)
         .mapToObj(i -> names[i])
         .collect(Collectors.toList());

Danh sách kết quả chỉ chứa "Erik".


Một cách khác có vẻ quen thuộc hơn khi bạn quen với các vòng lặp sẽ là duy trì bộ đếm ad hoc bằng cách sử dụng một đối tượng có thể thay đổi, ví dụ AtomicInteger:

String[] names = {"Sam", "Pamela", "Dave", "Pascal", "Erik"};
AtomicInteger index = new AtomicInteger();
List<String> list = Arrays.stream(names)
                          .filter(n -> n.length() <= index.incrementAndGet())
                          .collect(Collectors.toList());

Lưu ý rằng việc sử dụng phương thức sau trên luồng song song có thể bị phá vỡ vì các mục sẽ không được xử lý "theo thứ tự" .


28
Sử dụng nguyên tử theo cách này là có vấn đề với các luồng song song. Đầu tiên, thứ tự xử lý các phần tử sẽ không nhất thiết giống như thứ tự xuất hiện các phần tử trong mảng ban đầu. Do đó, "chỉ mục" được gán bằng nguyên tử có thể sẽ không khớp với chỉ số mảng thực tế. Thứ hai, trong khi các nguyên tử là an toàn luồng, bạn có thể gặp phải sự tranh chấp giữa nhiều luồng cập nhật nguyên tử, làm giảm số lượng song song.
Stuart Marks

1
Tôi đã phát triển một giải pháp tương tự như giải pháp của @assylias. Để tránh sự cố với luồng song song @StuartMarks đã đề cập, trước tiên tôi tạo một chuỗi tuần tự song song nhất định, thực hiện ánh xạ và khôi phục trạng thái song song. public static <T> Stream<Tuple2<Integer, T>> zipWithIndex(Stream<T> stream) { final AtomicInteger index = new AtomicInteger(); final Function<T, Tuple2<Integer, T>> zipper = e -> Tuples.of(index.getAndIncrement(), e); if (stream.isParallel()) { return stream.sequential().map(zipper).parallel(); } else { return stream.map(zipper); } }
Daniel Dietrich

4
@DanielDietrich Nếu bạn nghĩ rằng nó giải quyết được câu hỏi, bạn nên đăng nó dưới dạng câu trả lời thay vì bình luận (và mã cũng sẽ dễ đọc hơn!).
assylias

3
@DanielDietrich Xin lỗi, nếu tôi đọc mã đó một cách chính xác, nó sẽ không hoạt động. Bạn không thể có các phân đoạn khác nhau của một đường ống chạy song song và tuần tự. Chỉ cuối cùng parallelhoặc sequentialđược vinh danh khi hoạt động thiết bị đầu cuối bắt đầu.
Stuart Marks

4
Vì lợi ích của công lý, "cách sạch nhất" đã bị đánh cắp từ câu trả lời của @ Stuart.
Vadzim

70

API luồng Java 8 thiếu các tính năng nhận chỉ mục của thành phần luồng cũng như khả năng nén các luồng. Điều này thật đáng tiếc, vì nó làm cho các ứng dụng nhất định (như các thách thức LINQ) trở nên khó khăn hơn so với cách khác.

Tuy nhiên, thường có cách giải quyết. Thông thường, điều này có thể được thực hiện bằng cách "điều khiển" luồng với phạm vi số nguyên và lợi dụng thực tế là các phần tử gốc thường nằm trong một mảng hoặc trong một bộ sưu tập có thể truy cập theo chỉ mục. Ví dụ: vấn đề Thử thách 2 có thể được giải quyết theo cách này:

String[] names = {"Sam", "Pamela", "Dave", "Pascal", "Erik"};

List<String> nameList =
    IntStream.range(0, names.length)
        .filter(i -> names[i].length() <= i)
        .mapToObj(i -> names[i])
        .collect(toList());

Như tôi đã đề cập ở trên, điều này lợi dụng thực tế là nguồn dữ liệu (mảng tên) có thể lập chỉ mục trực tiếp. Nếu không, kỹ thuật này sẽ không hoạt động.

Tôi sẽ thừa nhận rằng điều này không thỏa mãn mục đích của Thử thách 2. Tuy nhiên, nó giải quyết vấn đề một cách hợp lý hiệu quả.

BIÊN TẬP

Ví dụ mã trước đây của tôi được sử dụng flatMapđể hợp nhất các hoạt động của bộ lọc và bản đồ, nhưng điều này rất cồng kềnh và không cung cấp lợi thế. Tôi đã cập nhật ví dụ cho mỗi nhận xét từ Holger.


7
Thế còn IntStream.range(0, names.length).filter(i->names[i].length()<=i).mapToObj(i->names[i])? Nó hoạt động mà không cần đấm bốc
Holger

1
Hừm, ừ, tại sao tôi lại nghĩ mình cần sử dụng flatMap?
Stuart Marks

2
Cuối cùng, xem lại điều này ... Tôi có thể đã sử dụng flatMapvì nó sắp xếp một hoạt động lọc và ánh xạ thành một hoạt động duy nhất, nhưng điều này thực sự không có lợi thế. Tôi sẽ chỉnh sửa ví dụ.
Stuart Marks

Stream.of (Array) sẽ tạo giao diện truyền phát cho một mảng. Hiệu quả làm cho nó thành Stream.of( names ).filter( n -> n.length() <= 1).collect( Collectors.toList() );ít hộp thư hơn và phân bổ bộ nhớ ít hơn; vì chúng tôi không tạo ra một luồng phạm vi nữa.
Mã Eyez

44

Từ ổi 21, bạn có thể sử dụng

Streams.mapWithIndex()

Ví dụ (từ tài liệu chính thức ):

Streams.mapWithIndex(
    Stream.of("a", "b", "c"),
    (str, index) -> str + ":" + index)
) // will return Stream.of("a:0", "b:1", "c:2")

3
Ngoài ra, người dân Guava chưa triển khai forEachWith Index (lấy người tiêu dùng thay vì chức năng), nhưng đó là vấn đề được chỉ định: github.com/google/guava/issues/2913 .
John Glassmyer

25

Tôi đã sử dụng giải pháp sau đây trong dự án của mình. Tôi nghĩ rằng nó là tốt hơn so với việc sử dụng các đối tượng có thể thay đổi hoặc phạm vi số nguyên.

import java.util.*;
import java.util.function.*;
import java.util.stream.Collector;
import java.util.stream.Collector.Characteristics;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
import static java.util.Objects.requireNonNull;


public class CollectionUtils {
    private CollectionUtils() { }

    /**
     * Converts an {@link java.util.Iterator} to {@link java.util.stream.Stream}.
     */
    public static <T> Stream<T> iterate(Iterator<? extends T> iterator) {
        int characteristics = Spliterator.ORDERED | Spliterator.IMMUTABLE;
        return StreamSupport.stream(Spliterators.spliteratorUnknownSize(iterator, characteristics), false);
    }

    /**
     * Zips the specified stream with its indices.
     */
    public static <T> Stream<Map.Entry<Integer, T>> zipWithIndex(Stream<? extends T> stream) {
        return iterate(new Iterator<Map.Entry<Integer, T>>() {
            private final Iterator<? extends T> streamIterator = stream.iterator();
            private int index = 0;

            @Override
            public boolean hasNext() {
                return streamIterator.hasNext();
            }

            @Override
            public Map.Entry<Integer, T> next() {
                return new AbstractMap.SimpleImmutableEntry<>(index++, streamIterator.next());
            }
        });
    }

    /**
     * Returns a stream consisting of the results of applying the given two-arguments function to the elements of this stream.
     * The first argument of the function is the element index and the second one - the element value. 
     */
    public static <T, R> Stream<R> mapWithIndex(Stream<? extends T> stream, BiFunction<Integer, ? super T, ? extends R> mapper) {
        return zipWithIndex(stream).map(entry -> mapper.apply(entry.getKey(), entry.getValue()));
    }

    public static void main(String[] args) {
        String[] names = {"Sam", "Pamela", "Dave", "Pascal", "Erik"};

        System.out.println("Test zipWithIndex");
        zipWithIndex(Arrays.stream(names)).forEach(entry -> System.out.println(entry));

        System.out.println();
        System.out.println("Test mapWithIndex");
        mapWithIndex(Arrays.stream(names), (Integer index, String name) -> index+"="+name).forEach((String s) -> System.out.println(s));
    }
}

+1 - đã có thể thực hiện một chức năng "chèn" một phần tử mỗi N chỉ mục bằng cách sử dụng StreamSupport.stream()và một trình vòng lặp tùy chỉnh.
ach

13

Ngoài protonpack, Seq của jOOλ cung cấp chức năng này (và bởi các thư viện mở rộng xây dựng trên nó như cyclops-Reac , tôi là tác giả của thư viện này).

Seq.seq(Stream.of(names)).zipWithIndex()
                         .filter( namesWithIndex -> namesWithIndex.v1.length() <= namesWithIndex.v2 + 1)
                         .toList();

Seq cũng chỉ hỗ trợ Seq.of (tên) và sẽ xây dựng Luồng JDK dưới vỏ bọc.

Tương đương phản ứng đơn giản sẽ trông giống như

 LazyFutureStream.of(names)
                 .zipWithIndex()
                 .filter( namesWithIndex -> namesWithIndex.v1.length() <= namesWithIndex.v2 + 1)
                 .toList();

Phiên bản phản ứng đơn giản được điều chỉnh phù hợp hơn cho xử lý không đồng bộ / đồng thời.


John tôi thấy hôm nay thư viện của bạn, tôi vừa ngạc nhiên vừa bối rối.
GOXR3PLUS

12

Chỉ để hoàn thiện đây là giải pháp liên quan đến thư viện StreamEx của tôi :

String[] names = {"Sam","Pamela", "Dave", "Pascal", "Erik"};
EntryStream.of(names)
    .filterKeyValue((idx, str) -> str.length() <= idx+1)
    .values().toList();

Ở đây chúng tôi tạo ra một EntryStream<Integer, String>phần mở rộng Stream<Entry<Integer, String>>và thêm một số hoạt động cụ thể như filterKeyValuehoặc values. Ngoài ra toList()phím tắt được sử dụng.


công việc tuyệt vời; Có một lối tắt cho .forEach(entry -> {}) ?
Steve Oh

2
@SteveOh nếu tôi hiểu bạn hỏi chính xác, thì có, bạn có thể viết .forKeyValue((key, value) -> {}).
Tagir Valeev 29/03/18

8

Tôi đã tìm thấy các giải pháp ở đây khi Stream được tạo danh sách hoặc mảng (và bạn biết kích thước). Nhưng nếu Stream có kích thước không xác định thì sao? Trong trường hợp này hãy thử biến thể này:

public class WithIndex<T> {
    private int index;
    private T value;

    WithIndex(int index, T value) {
        this.index = index;
        this.value = value;
    }

    public int index() {
        return index;
    }

    public T value() {
        return value;
    }

    @Override
    public String toString() {
        return value + "(" + index + ")";
    }

    public static <T> Function<T, WithIndex<T>> indexed() {
        return new Function<T, WithIndex<T>>() {
            int index = 0;
            @Override
            public WithIndex<T> apply(T t) {
                return new WithIndex<>(index++, t);
            }
        };
    }
}

Sử dụng:

public static void main(String[] args) {
    Stream<String> stream = Stream.of("a", "b", "c", "d", "e");
    stream.map(WithIndex.indexed()).forEachOrdered(e -> {
        System.out.println(e.index() + " -> " + e.value());
    });
}

6

Với một danh sách bạn có thể thử

List<String> strings = new ArrayList<>(Arrays.asList("First", "Second", "Third", "Fourth", "Fifth")); // An example list of Strings
strings.stream() // Turn the list into a Stream
    .collect(HashMap::new, (h, o) -> h.put(h.size(), o), (h, o) -> {}) // Create a map of the index to the object
        .forEach((i, o) -> { // Now we can use a BiConsumer forEach!
            System.out.println(String.format("%d => %s", i, o));
        });

Đầu ra:

0 => First
1 => Second
2 => Third
3 => Fourth
4 => Fifth

1
Trên thực tế là một ý tưởng hay, nhưng chuỗi :: indexOf có thể hơi đắt. Đề nghị của tôi là sử dụng thay thế: .collect (HashMap :: new, (h, s) -> h.put (h.size (), s), (h, s) -> {}) . Bạn chỉ có thể sử dụng phương thức size () để tạo chỉ mục.
gil.fernandes

@ gil.fernandes Cảm ơn bạn đã gợi ý. Tôi sẽ thực hiện các chỉnh sửa.
V0idst4r

3

Không có cách nào lặp đi lặp lại Streamtrong khi có quyền truy cập vào chỉ mục bởi vì Streamkhông giống như bất kỳ Collection. A Streamchỉ đơn thuần là một đường ống để mang dữ liệu từ nơi này đến nơi khác, như đã nêu trong tài liệu :

Không lưu trữ. Luồng không phải là cấu trúc dữ liệu lưu trữ các phần tử; thay vào đó, chúng mang các giá trị từ một nguồn (có thể là cấu trúc dữ liệu, trình tạo, kênh IO, v.v.) thông qua một hệ thống các hoạt động tính toán.

Tất nhiên, khi bạn có vẻ gợi ý trong câu hỏi của mình, bạn luôn có thể chuyển đổi Stream<V>thành a Collection<V>, chẳng hạn như a List<V>, trong đó bạn sẽ có quyền truy cập vào các chỉ mục.


2
Điều này có sẵn trong các ngôn ngữ / công cụ khác. Nó chỉ đơn giản là một giá trị gia tăng được truyền cho chức năng bản đồ
Lee Campbell

Liên kết của bạn đến tài liệu bị hỏng.
Usman Mutawakil

3

Với https://github.com/poetix/protonpack bạn có thể thực hiện mã zip đó:

String[] names = {"Sam","Pamela", "Dave", "Pascal", "Erik"};

List<String> nameList;
Stream<Integer> indices = IntStream.range(0, names.length).boxed(); 

nameList = StreamUtils.zip(indices, stream(names),SimpleEntry::new)
        .filter(e -> e.getValue().length() <= e.getKey()).map(Entry::getValue).collect(toList());                   

System.out.println(nameList);

3

Nếu bạn không phiền khi sử dụng thư viện của bên thứ ba, Bộ sưu tập EclipsezipWithIndexforEachWithIndexcó sẵn để sử dụng trên nhiều loại. Đây là một tập hợp các giải pháp cho thách thức này cho cả các loại JDK và các loại Bộ sưu tập Eclipse bằng cách sử dụng zipWithIndex.

String[] names = { "Sam", "Pamela", "Dave", "Pascal", "Erik" };
ImmutableList<String> expected = Lists.immutable.with("Erik");
Predicate<Pair<String, Integer>> predicate =
    pair -> pair.getOne().length() <= pair.getTwo() + 1;

// JDK Types
List<String> strings1 = ArrayIterate.zipWithIndex(names)
    .collectIf(predicate, Pair::getOne);
Assert.assertEquals(expected, strings1);

List<String> list = Arrays.asList(names);
List<String> strings2 = ListAdapter.adapt(list)
    .zipWithIndex()
    .collectIf(predicate, Pair::getOne);
Assert.assertEquals(expected, strings2);

// Eclipse Collections types
MutableList<String> mutableNames = Lists.mutable.with(names);
MutableList<String> strings3 = mutableNames.zipWithIndex()
    .collectIf(predicate, Pair::getOne);
Assert.assertEquals(expected, strings3);

ImmutableList<String> immutableNames = Lists.immutable.with(names);
ImmutableList<String> strings4 = immutableNames.zipWithIndex()
    .collectIf(predicate, Pair::getOne);
Assert.assertEquals(expected, strings4);

MutableList<String> strings5 = mutableNames.asLazy()
    .zipWithIndex()
    .collectIf(predicate, Pair::getOne, Lists.mutable.empty());
Assert.assertEquals(expected, strings5);

Đây là một giải pháp sử dụng forEachWithIndexthay thế.

MutableList<String> mutableNames =
    Lists.mutable.with("Sam", "Pamela", "Dave", "Pascal", "Erik");
ImmutableList<String> expected = Lists.immutable.with("Erik");

List<String> actual = Lists.mutable.empty();
mutableNames.forEachWithIndex((name, index) -> {
        if (name.length() <= index + 1)
            actual.add(name);
    });
Assert.assertEquals(expected, actual);

Nếu bạn thay đổi lambdas thành các lớp bên trong ẩn danh ở trên, thì tất cả các ví dụ mã này cũng sẽ hoạt động trong Java 5 - 7.

Lưu ý: Tôi là người đi làm cho Bộ sưu tập Eclipse


2

Nếu bạn tình cờ sử dụng Vavr (trước đây gọi là Javaslang), bạn có thể tận dụng phương thức chuyên dụng:

Stream.of("A", "B", "C")
  .zipWithIndex();

Nếu chúng tôi in ra nội dung, chúng tôi sẽ thấy một điều thú vị:

Stream((A, 0), ?)

Điều này là do Streamslười biếng và chúng tôi không có manh mối về các mục tiếp theo trong luồng.


1

Nếu bạn đang cố gắng lấy chỉ mục dựa trên một vị ngữ, hãy thử điều này:

Nếu bạn chỉ quan tâm đến chỉ số đầu tiên:

OptionalInt index = IntStream.range(0, list.size())
    .filter(i -> list.get(i) == 3)
    .findFirst();

Hoặc nếu bạn muốn tìm nhiều chỉ mục:

IntStream.range(0, list.size())
   .filter(i -> list.get(i) == 3)
   .collect(Collectors.toList());

Thêm .orElse(-1);trong trường hợp bạn muốn trả về một giá trị nếu nó không tìm thấy nó.


1

Đây là mã của AbacusUtil

Stream.of(names).indexed()
      .filter(e -> e.value().length() <= e.index())
      .map(Indexed::value).toList();

Tiết lộ tiết lộ Tôi là nhà phát triển của AbacusUtil.


1

Bạn có thể sử dụng IntStream.iterate()để lấy chỉ mục:

String[] names = {"Sam","Pamela", "Dave", "Pascal", "Erik"};
List<String> nameList = IntStream.iterate(0, i -> i < names.length, i -> i + 1)
        .filter(i -> names[i].length() <= i)
        .mapToObj(i -> names[i])
        .collect(Collectors.toList());

Điều này chỉ hoạt động cho Java 9 trở lên trong Java 8, bạn có thể sử dụng điều này:

String[] names = {"Sam","Pamela", "Dave", "Pascal", "Erik"};
List<String> nameList = IntStream.iterate(0, i -> i + 1)
        .limit(names.length)
        .filter(i -> names[i].length() <= i)
        .mapToObj(i -> names[i])
        .collect(Collectors.toList());

0

Bạn có thể tạo một lớp bên trong tĩnh để đóng gói bộ chỉ mục như tôi cần làm trong ví dụ dưới đây:

static class Indexer {
    int i = 0;
}

public static String getRegex() {
    EnumSet<MeasureUnit> range = EnumSet.allOf(MeasureUnit.class);
    StringBuilder sb = new StringBuilder();
    Indexer indexer = new Indexer();
    range.stream().forEach(
            measureUnit -> {
                sb.append(measureUnit.acronym);
                if (indexer.i < range.size() - 1)
                    sb.append("|");

                indexer.i++;
            }
    );
    return sb.toString();
}

0

Câu hỏi này ( Cách truyền phát để lấy chỉ mục của phần tử đầu tiên khớp với boolean ) đã đánh dấu câu hỏi hiện tại là trùng lặp, vì vậy tôi không thể trả lời nó ở đó; Tôi đang trả lời nó ở đây.

Đây là một giải pháp chung để có được chỉ mục phù hợp mà không cần thư viện bên ngoài.

Nếu bạn có một danh sách.

public static <T> int indexOf(List<T> items, Predicate<T> matches) {
        return IntStream.range(0, items.size())
                .filter(index -> matches.test(items.get(index)))
                .findFirst().orElse(-1);
}

Và gọi nó như thế này:

int index = indexOf(myList, item->item.getId()==100);

Và nếu sử dụng một bộ sưu tập, hãy thử cái này.

   public static <T> int indexOf(Collection<T> items, Predicate<T> matches) {
        int index = -1;
        Iterator<T> it = items.iterator();
        while (it.hasNext()) {
            index++;
            if (matches.test(it.next())) {
                return index;
            }
        }
        return -1;
    }

0

Một cách có thể là lập chỉ mục cho từng phần tử trên luồng:

AtomicInteger index = new AtomicInteger();
Stream.of(names)
  .map(e->new Object() { String n=e; public i=index.getAndIncrement(); })
  .filter(o->o.n.length()<=o.i) // or do whatever you want with pairs...
  .forEach(o->System.out.println("idx:"+o.i+" nam:"+o.n));

Sử dụng một lớp ẩn danh dọc theo luồng không được sử dụng tốt trong khi rất hữu ích.


0

bạn không cần map nhất thiết phải
là lambda gần nhất với ví dụ LINQ:

int[] idx = new int[] { 0 };
Stream.of( names ).filter( name -> name.length() <= idx[0]++ ).collect( Collectors.toList() );

0
String[] namesArray = {"Sam","Pamela", "Dave", "Pascal", "Erik"};
String completeString
         =  IntStream.range(0,namesArray.length)
           .mapToObj(i -> namesArray[i]) // Converting each array element into Object
           .map(String::valueOf) // Converting object to String again
           .collect(Collectors.joining(",")); // getting a Concat String of all values
        System.out.println(completeString);

ĐẦU RA: Sam, Pamela, Dave, Pascal, Erik

String[] namesArray = {"Sam","Pamela", "Dave", "Pascal", "Erik"};

IntStream.range(0,namesArray.length)
               .mapToObj(i -> namesArray[i]) // Converting each array element into Object
               .map(String::valueOf) // Converting object to String again
               .forEach(s -> {
                //You can do various operation on each element here
                System.out.println(s);
               }); // getting a Concat String of all 

Để thu thập trong danh sách:

String[] namesArray = {"Sam","Pamela", "Dave", "Pascal", "Erik"};
 List<String> namesList
                =  IntStream.range(0,namesArray.length)
                .mapToObj(i -> namesArray[i]) // Converting each array element into Object
                .map(String::valueOf) // Converting object to String again
                .collect(Collectors.toList()); // collecting elements in List
        System.out.println(listWithIndex);

Giải pháp cho câu hỏi trên dự kiến ​​sẽ Listchứa một yếu tố Erik .
Kaplan

Tôi cũng đã thêm một ví dụ để thu thập trong Danh sách.
Arpan Saini

0

Như jean-baptiste-yunès đã nói, nếu luồng của bạn dựa trên Danh sách java thì sử dụng AtomicInteger và phương thức gia tăngAndGet của nó là một giải pháp rất tốt cho vấn đề và số nguyên được trả về sẽ tương ứng với chỉ mục trong Danh sách gốc miễn là bạn không sử dụng một luồng song song.


0

Nếu bạn cần chỉ mục trong forEach thì điều này cung cấp một cách.

  public class IndexedValue {

    private final int    index;
    private final Object value;

    public IndexedValue(final int index, final Object value) { 
        this.index = index;
        this.value = value;
    }

    public int getIndex() {
        return index;
    }

    public Object getValue() {
        return value;
    }
}

Sau đó sử dụng nó như sau.

@Test
public void withIndex() {
    final List<String> list = Arrays.asList("a", "b");
    IntStream.range(0, list.size())
             .mapToObj(index -> new IndexedValue(index, list.get(index)))
             .forEach(indexValue -> {
                 System.out.println(String.format("%d, %s",
                                                  indexValue.getIndex(),
                                                  indexValue.getValue().toString()));
             });
}
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.