มีวิธีรัดกุมในการวนซ้ำกระแสกับดัชนีใน Java 8 หรือไม่?


382

มีวิธีรัดกุมในการวนซ้ำสตรีมในขณะที่สามารถเข้าถึงดัชนีในสตรีมได้หรือไม่

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

ซึ่งดูเหมือนว่าค่อนข้างน่าผิดหวังเมื่อเทียบกับตัวอย่างของ LINQ ที่ให้ไว้

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

มีวิธีที่กระชับกว่านี้ไหม?

ดูเหมือนว่าซิปจะถูกย้ายหรือถูกลบออก ...


2
คือintRange()อะไร ยังไม่ได้ใช้วิธีนี้ใน Java 8 จนถึงตอนนี้
Rohit Jain

@RohitJain อาจ IntStream.rangeClosed(x, y)
assylias

2
ในฐานะที่เป็นความคิดเห็นด้านความท้าทาย 4 ดูดีขึ้น (IMO) ด้วยList<String> allCities = map.values().stream().flatMap(list -> list.stream()).collect(Collectors.toList());
assylias

3
ใช่zipจะถูกลบออกพร้อมกับการทดลองลำธารสองมูลค่านานัปการเรียกหรือBiStream MapStreamปัญหาหลักคือการทำเช่นนี้ได้อย่างมีประสิทธิภาพ Java ต้องการประเภทคู่ (หรือทูเปิล) ที่มีโครงสร้างจริงๆ หากขาดไปแล้วมันเป็นเรื่องง่ายที่จะสร้างคลาส Pair หรือ Tuple สามัญ - ทำได้หลายครั้ง - แต่พวกเขาทั้งหมดจะลบประเภทเดียวกัน
Stuart Marks

3
โอ้ปัญหาอีกอย่างหนึ่งของคลาส Pair หรือ Tuple คลาสก็คือมันต้องการกล่องดั้งเดิมทั้งหมด
Stuart Marks

คำตอบ:


435

วิธีที่สะอาดที่สุดคือเริ่มจากดัชนีหลาย ๆ

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

รายการผลลัพธ์ประกอบด้วย "Erik" เท่านั้น


อีกทางเลือกหนึ่งซึ่งมีลักษณะที่คุ้นเคยมากขึ้นเมื่อคุณจะใช้สำหรับลูปจะรักษาเคาน์เตอร์เฉพาะกิจโดยใช้วัตถุที่ไม่แน่นอนเช่น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());

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


28
การใช้ atomics ด้วยวิธีนี้เป็นปัญหากับลำธารขนาน อันดับแรกลำดับการประมวลผลขององค์ประกอบไม่จำเป็นต้องเหมือนกับลำดับที่องค์ประกอบเกิดขึ้นในอาร์เรย์เริ่มต้น ดังนั้น "ดัชนี" ที่กำหนดโดยใช้อะตอมอาจไม่ตรงกับดัชนีอาร์เรย์จริง ข้อที่สองในขณะที่อะตอมมิกส์มีความปลอดภัยต่อเธรดคุณอาจพบกับความขัดแย้งในหลายเธรดที่อัปเดตอะตอมมิกลดระดับความขนาน
Stuart Marks

1
ฉันพัฒนาโซลูชันคล้ายกับ @assylias เพื่อหลีกเลี่ยงปัญหาด้วยขนานกระแส @StuartMarks กล่าวถึงฉันก่อนทำลำดับกระแสขนานที่กำหนดดำเนินการทำแผนที่และเรียกคืนสถานะขนาน 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 ถ้าคุณคิดว่ามันแก้คำถามคุณควรโพสต์มันเป็นคำตอบมากกว่าความคิดเห็น (และรหัสจะอ่านง่ายขึ้นด้วย!)
assylias

3
@DanielDietrich ขออภัยถ้าฉันอ่านรหัสนั้นถูกต้องมันจะไม่ทำงาน คุณไม่สามารถมีเซ็กเมนต์ที่แตกต่างกันของไปป์ไลน์ที่ทำงานในแบบขนานเทียบกับลำดับ เฉพาะคนสุดท้ายของparallelหรือsequentialรู้สึกเป็นเกียรติเมื่อการดำเนินการของสถานีเริ่มต้น
Stuart Marks

4
เพื่อความยุติธรรม "วิธีที่สะอาดที่สุด" ถูกขโมยไปจากคำตอบของ @ Stuart
Vadzim

70

Java 8 สตรีม API ขาดคุณสมบัติในการรับดัชนีขององค์ประกอบสตรีมรวมทั้งความสามารถในการซิปสตรีมเข้าด้วยกัน นี่เป็นเรื่องที่โชคร้ายเพราะมันทำให้แอพพลิเคชั่นบางอย่าง (เช่นความท้าทาย LINQ) ยากกว่าที่พวกเขาจะเป็นอย่างอื่น

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

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

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

ฉันจะยอมรับว่าสิ่งนี้ไม่เป็นไปตามเจตนารมย์ของการท้าทาย 2 อย่างไรก็ตามมันแก้ปัญหาได้อย่างมีประสิทธิภาพพอสมควร

แก้ไข

ตัวอย่างรหัสก่อนหน้าของฉันใช้flatMapเพื่อหลอมการดำเนินการตัวกรองและแผนที่ แต่สิ่งนี้ยุ่งยากและไม่มีประโยชน์ ฉันได้อัปเดตตัวอย่างตามความคิดเห็นจาก Holger


7
แล้วไงIntStream.range(0, names.length).filter(i->names[i].length()<=i).mapToObj(i->names[i])ล่ะ มันทำงานได้โดยไม่ต้องชกมวย…
Holger

1
อืมใช่แล้วทำไมฉันถึงคิดว่าฉันต้องใช้flatMapล่ะ
Stuart Marks

2
ในที่สุดก็กลับมาทบทวนสิ่งนี้ ... ฉันอาจใช้flatMapเพราะการเรียงลำดับของการดำเนินการกรองและการแมปเป็นการดำเนินการครั้งเดียว แต่นี่ไม่ได้ประโยชน์จริงๆ ฉันจะแก้ไขตัวอย่าง
Stuart Marks

Stream.of (Array) จะสร้างสตรีมอินเตอร์เฟสสำหรับอาร์เรย์ ทำให้เป็นStream.of( names ).filter( n -> n.length() <= 1).collect( Collectors.toList() );unboxing น้อยลงและมีการจัดสรรหน่วยความจำน้อย เนื่องจากเราไม่ได้สร้างช่วงสตรีมอีกต่อไป
รหัส Eyez

44

ตั้งแต่ฝรั่ง 21 คุณสามารถใช้

Streams.mapWithIndex()

ตัวอย่าง (จากเอกสารทางการ ):

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

3
นอกจากนี้คนที่ฝรั่งยังไม่ได้ดำเนินการ forEachWithIndex (การเป็นผู้บริโภคมากกว่าฟังก์ชั่น) แต่มันเป็นปัญหาที่ได้รับมอบหมาย: github.com/google/guava/issues/2913
John Glassmyer

25

ฉันใช้วิธีแก้ไขปัญหาต่อไปนี้ในโครงการของฉัน ฉันคิดว่ามันจะดีกว่าการใช้วัตถุที่ไม่แน่นอนหรือช่วงจำนวนเต็ม

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 - สามารถใช้ฟังก์ชันที่ "แทรก" องค์ประกอบทุกดัชนี N ที่ใช้StreamSupport.stream()และตัววนซ้ำที่กำหนดเอง
34414 ach

13

นอกจาก protonpack แล้วSeq ของjOOλยังมีฟังก์ชั่นนี้ (และโดยส่วนขยายของไลบรารีที่สร้างขึ้นมาเช่นcyclops-reactฉันเป็นผู้เขียนของไลบรารีนี้)

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

Seq ยังรองรับเพียง Seq.of (ชื่อ) และจะสร้างกระแส JDK ภายใต้หน้ากาก

ความเรียบง่ายที่เทียบเท่ากันจะมีลักษณะคล้ายกัน

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

เวอร์ชั่นที่ตอบสนองง่ายถูกปรับให้เหมาะสมกับการประมวลผลแบบอะซิงโครนัส / พร้อมกันมากขึ้น


John ฉันเห็นห้องสมุดของคุณวันนี้ฉันทั้งทึ่งและสับสน
GOXR3PLUS

12

เพื่อความสมบูรณ์นี่คือวิธีแก้ปัญหาที่เกี่ยวข้องกับไลบรารีStreamExของฉัน:

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

ที่นี่เราสร้างEntryStream<Integer, String>ซึ่งขยายStream<Entry<Integer, String>>และเพิ่มการดำเนินงานเฉพาะบางอย่างเช่นหรือfilterKeyValue valuesนอกจากนี้ยังใช้toList()ปุ่มลัด


การทำงานที่ดี; มีทางลัดสำหรับ.forEach(entry -> {}) หรือไม่?
Steve Oh

2
@SteveOh .forKeyValue((key, value) -> {})ถ้าผมเข้าใจว่าคุณตั้งคำถามได้อย่างถูกต้องแล้วใช่คุณสามารถเขียน
Tagir Valeev

8

ฉันพบวิธีแก้ปัญหาที่นี่เมื่อสร้างกระแสรายการหรืออาร์เรย์ (และคุณรู้ขนาด) แต่ถ้ากระแสมีขนาดที่ไม่รู้จัก ในกรณีนี้ลองใช้ตัวแปรนี้:

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

การใช้งาน:

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

ด้วยรายการคุณสามารถลอง

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

เอาท์พุท:

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

1
จริง ๆ แล้วเป็นความคิดที่ดี แต่สตริง :: indexOfอาจมีราคาแพงเล็กน้อย คำแนะนำของฉันคือการใช้แทน: .collect (HashMap :: ใหม่ (h, S) -> h.put (h.size (), s), (H, S) -> {}) คุณสามารถใช้วิธี size () เพื่อสร้างดัชนี
gil.fernandes

@ gil.fernandes ขอบคุณสำหรับคำแนะนำ ฉันจะแก้ไข
V0idst4r

3

ไม่มีทางที่จะย้ำไปที่Streamการเข้าถึงในขณะที่มีการจัดทำดัชนีเพราะจะแตกต่างใด ๆStream CollectionA Streamเป็นเพียงท่อส่งข้อมูลจากที่หนึ่งไปอีกที่หนึ่งตามที่ระบุไว้ในเอกสารประกอบ :

ไม่มีที่เก็บ กระแสข้อมูลไม่ใช่โครงสร้างข้อมูลที่เก็บองค์ประกอบ แต่จะมีค่าจากแหล่งที่มา (ซึ่งอาจเป็นโครงสร้างข้อมูลตัวสร้างช่องสัญญาณ IO ฯลฯ ) ผ่านไปป์ไลน์ของการดำเนินการคำนวณ

แน่นอนว่าเมื่อคุณดูเหมือนจะบอกใบ้ในคำถามของคุณคุณสามารถแปลงStream<V>เป็น a Collection<V>เช่น a List<V>ซึ่งคุณจะสามารถเข้าถึงดัชนีได้ตลอดเวลา


2
มีให้ในภาษา / เครื่องมืออื่น ๆ มันเป็นเพียงการเพิ่มค่าที่ส่งผ่านไปยังฟังก์ชันแผนที่
Lee Campbell

ลิงก์ของคุณไปยังเอกสารนั้นใช้งานไม่ได้
Usman Mutawakil

3

ด้วยhttps://github.com/poetix/protonpack u คุณสามารถทำได้ดังนี้:

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

หากคุณไม่สนใจการใช้ห้องสมุดบุคคลที่สามคอลเลกชัน EclipseมีzipWithIndexและforEachWithIndexพร้อมใช้งานในหลายประเภท นี่เป็นชุดของการแก้ปัญหาเพื่อความท้าทายนี้ทั้งสองประเภท JDK 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);

นี่เป็นวิธีแก้ปัญหาที่ใช้forEachWithIndexแทน

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

หากคุณเปลี่ยน lambdas เป็นคลาสภายในที่ไม่ระบุชื่อด้านบนตัวอย่างโค้ดเหล่านี้ทั้งหมดจะใช้ได้ใน Java 5 - 7 เช่นกัน

หมายเหตุ:ฉันเป็นคอมมิชชันสำหรับ Eclipse Collections


2

หากคุณเคยใช้ Vavr (เดิมชื่อ Javaslang) คุณสามารถใช้วิธีการเฉพาะ:

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

หากเราพิมพ์เนื้อหาออกมาเราจะเห็นสิ่งที่น่าสนใจ:

Stream((A, 0), ?)

เป็นเพราะStreamsขี้เกียจและเราไม่มีเงื่อนงำเกี่ยวกับรายการถัดไปในสตรีม


1

หากคุณพยายามรับดัชนีโดยใช้เพรดิเคตให้ลองสิ่งนี้:

หากคุณสนใจเฉพาะดัชนีแรก:

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

หรือถ้าคุณต้องการค้นหาหลายดัชนี:

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

เพิ่ม.orElse(-1);ในกรณีที่คุณต้องการส่งคืนค่าหากไม่พบ


1

นี่คือรหัสโดยAbacusUtil

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

การเปิดเผยข้อมูล: ฉันเป็นผู้พัฒนา AbacusUtil


1

คุณสามารถใช้IntStream.iterate()เพื่อรับดัชนี:

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

ใช้งานได้กับ Java 9 ขึ้นไปใน Java 8 คุณสามารถใช้สิ่งนี้:

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

คุณสามารถสร้างคลาสภายในแบบคงที่เพื่อแค็ปซูลตัวสร้างดัชนีตามที่ฉันต้องทำในตัวอย่างด้านล่าง:

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

คำถามนี้ ( Stream Way เพื่อรับดัชนีขององค์ประกอบแรกที่ตรงกับบูลีน ) ได้ทำเครื่องหมายคำถามปัจจุบันว่าเป็นคำถามซ้ำดังนั้นฉันจึงไม่สามารถตอบคำถามนั้นได้ ฉันตอบมันที่นี่

นี่เป็นวิธีการทั่วไปในการรับดัชนีการจับคู่ที่ไม่ต้องการไลบรารีภายนอก

หากคุณมีรายการ

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

และเรียกมันว่าสิ่งนี้:

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

และถ้าใช้คอลเล็กชันให้ลองอันนี้

   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

วิธีหนึ่งที่เป็นไปได้คือการจัดทำดัชนีแต่ละองค์ประกอบในโฟลว์:

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

การใช้คลาสที่ไม่ระบุชื่อตามลำธารนั้นไม่ได้ใช้อย่างดีในขณะที่มีประโยชน์มาก


0

คุณไม่จำเป็นต้อง map จำเป็นต้อง
ว่าเป็นแลมบ์ดาที่อยู่ใกล้กับตัวอย่าง 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);

ผลลัพธ์: 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 

ในการรวบรวมในรายการ:

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

วิธีการแก้ปัญหาของคำถามข้างต้นคาดว่าจะเป็นListที่มีองค์ประกอบหนึ่งเอริค
Kaplan

ฉันได้เพิ่มตัวอย่างเพื่อรวบรวมในรายการด้วย
Arpan Saini

0

ดังที่ jean-baptiste-yunèsกล่าวว่าหากสตรีมของคุณใช้รายชื่อ java แล้วใช้ AtomicInteger และวิธี incrementAndGet เป็นวิธีแก้ปัญหาที่ดีมากและจำนวนเต็มคืนมานั้นจะตรงกับดัชนีในรายการดั้งเดิมตราบเท่าที่คุณ ห้ามใช้สตรีมแบบขนาน


0

หากคุณต้องการดัชนีใน forEach นี่จะให้วิธีการ

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

จากนั้นใช้งานได้ดังนี้

@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()));
             });
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.