(#) Broken Iterator !!! WARNING: Broken Iterator This is a warning. Id : `BrokenIterator` Summary : Broken Iterator Severity : Warning Category : Correctness Platform : Android Vendor : Android Open Source Project Feedback : https://issuetracker.google.com/issues/new?component=192708 Since : 3.6.0 (February 2020) Affects : Kotlin and Java files Editing : This check runs on the fly in the IDE editor See : https://developer.android.com/reference/java/util/LinkedHashMap Implementation : [Source Code](https://cs.android.com/android-studio/platform/tools/base/+/mirror-goog-studio-main:lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/IteratorDetector.kt) Tests : [Source Code](https://cs.android.com/android-studio/platform/tools/base/+/mirror-goog-studio-main:lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/IteratorDetectorTest.kt) **For LinkedHashMap:** The spliterators returned by `LinkedHashMap` in Android Nougat (API levels 24 and 25) use the wrong order (inconsistent with the iterators, which use the correct order), despite reporting `Spliterator.ORDERED`. You may use the following code fragments to obtain a correctly ordered `Spliterator` on API level 24 and 25: For a Collection view `c = lhm.entrySet()`, `c = lhm.keySet()` or `c = lhm.values()`, use `java.util.Spliterators.spliterator(c, c.spliterator().characteristics())` instead of `c.spliterator()`. Instead of `c.stream()` or `c.parallelStream()`, use `java.util.stream.StreamSupport.stream(spliterator, false)` to construct a (nonparallel) Stream from such a `Spliterator`. **For Vector:** The `listIterator()` returned for a `Vector` has a broken `add()` implementation on Android N (API level 24). Consider switching to `ArrayList` and if necessary adding synchronization. (##) Example Here is an example of lint warnings produced by this check: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~text src/test/pkg/LinkedHashmapTest.java:34:Warning: LinkedHashMap#spliterator was broken in API 24 and 25. Workaround: Use java.util.Spliterators.spliterator(c2a, c2a.spliterator().characteristics()) [BrokenIterator] Spliterator<String> keys2a = c2a.spliterator(); // Warn ----------------- src/test/pkg/LinkedHashmapTest.java:35:Warning: LinkedHashMap#spliterator was broken in API 24 and 25. Workaround: Use java.util.Spliterators.spliterator(c2b, c2b.spliterator().characteristics()) [BrokenIterator] Spliterator<String> keys2b = c2b.spliterator(); // Warn ----------------- src/test/pkg/LinkedHashmapTest.java:36:Warning: LinkedHashMap#spliterator was broken in API 24 and 25. Workaround: Use java.util.Spliterators.spliterator(c2c, c2c.spliterator().characteristics()) [BrokenIterator] Spliterator<Entry<String, String>> keys2c = c2c.spliterator(); // Warn ----------------- src/test/pkg/LinkedHashmapTest.java:39:Warning: LinkedHashMap#stream was broken in API 24 and 25. Workaround: Use java.util.stream.StreamSupport.stream(spliterator, false) [BrokenIterator] Stream<String> stream1 = c2a.stream(); // Warn ------------ src/test/pkg/LinkedHashmapTest.java:40:Warning: LinkedHashMap#spliterator was broken in API 24 and 25. Workaround: Use java.util.Spliterators.spliterator(c2a, c2a.spliterator().characteristics()) [BrokenIterator] StreamSupport.stream(c2a.spliterator(), false); // Warn ----------------- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Here are the relevant source files: `src/test/pkg/LinkedHashmapTest.java`: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~java linenumbers package test.pkg; import android.os.Build; import androidx.annotation.RequiresApi; import java.util.Collection; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.Spliterator; import java.util.Spliterators; import java.util.stream.Stream; import java.util.stream.StreamSupport; public class LinkedHashmapTest { @RequiresApi(api = Build.VERSION_CODES.N) public void test() { Map map1 = new HashMap<>(); Set c1a = map1.keySet(); Collection c1b = map1.values(); Set> c1c = map1.entrySet(); Spliterator keys1a = c1a.spliterator(); Spliterator keys1b = c1b.spliterator(); Spliterator> keys1c = c1c.spliterator(); // OK (not a LinkedHashMap) Spliterator keys1 = Spliterators.spliterator(c1a, c1a.spliterator().characteristics());// OK Map map2 = new LinkedHashMap<>(); Set c2a = map2.keySet(); Collection c2b = map2.values(); Set> c2c = map2.entrySet(); Spliterator keys2a = c2a.spliterator(); // Warn Spliterator keys2b = c2b.spliterator(); // Warn Spliterator> keys2c = c2c.spliterator(); // Warn Spliterator keys2 = Spliterators.spliterator(c2a, c2a.spliterator().characteristics());// OK Stream stream1 = c2a.stream(); // Warn StreamSupport.stream(c2a.spliterator(), false); // Warn Spliterators.spliterator(c2a, c2a.spliterator().characteristics()); // OK StreamSupport.stream(keys2, false); // OK } } ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ `src/test/pkg/kt/LinkedHashmapTest.kt`: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~kotlin linenumbers package test.pkg.kt import android.os.Build import androidx.annotation.RequiresApi import java.util.* import java.util.stream.StreamSupport class LinkedHashmapTest { @RequiresApi(api = Build.VERSION_CODES.N) fun test() { val map1 = HashMap() val c1a = map1.keys val c1b = map1.values val c1c = map1.entries val keys1a = c1a.spliterator() val keys1b = c1b.spliterator() val keys1c = c1c.spliterator() // OK (not a LinkedHashMap) val keys1 = Spliterators.spliterator(c1a, c1a.spliterator().characteristics())// OK val map2 = LinkedHashMap() val c2a = map2.keys val c2b = map2.values val c2c = map2.entries val keys2a = c2a.spliterator() // Warn val keys2b = c2b.spliterator() // Warn val keys2c = c2c.spliterator() // Warn val keys2 = Spliterators.spliterator(c2a, c2a.spliterator().characteristics())// OK val stream1 = c2a.stream() // Warn StreamSupport.stream(c2a.spliterator(), false) // Warn Spliterators.spliterator(c2a, c2a.spliterator().characteristics()) // OK StreamSupport.stream(keys2, false) // OK } } ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ You can also visit the [source code](https://cs.android.com/android-studio/platform/tools/base/+/mirror-goog-studio-main:lint/libs/lint-tests/src/test/java/com/android/tools/lint/checks/IteratorDetectorTest.kt) for the unit tests for this check to see additional scenarios. The above example was automatically extracted from the first unit test found for this lint check, `IteratorDetector.testLinkedHashMap`. To report a problem with this extracted sample, visit https://issuetracker.google.com/issues/new?component=192708. (##) Suppressing You can suppress false positives using one of the following mechanisms: * Using a suppression annotation like this on the enclosing element: ```kt // Kotlin @Suppress("BrokenIterator") fun method() { add(...) } ``` or ```java // Java @SuppressWarnings("BrokenIterator") void method() { add(...); } ``` * Using a suppression comment like this on the line above: ```kt //noinspection BrokenIterator problematicStatement() ``` * Using a special `lint.xml` file in the source tree which turns off the check in that folder and any sub folder. A simple file might look like this: ```xml <?xml version="1.0" encoding="UTF-8"?> <lint> <issue id="BrokenIterator" severity="ignore" /> </lint> ``` Instead of `ignore` you can also change the severity here, for example from `error` to `warning`. You can find additional documentation on how to filter issues by path, regular expression and so on [here](https://googlesamples.github.io/android-custom-lint-rules/usage/lintxml.md.html). * In Gradle projects, using the DSL syntax to configure lint. For example, you can use something like ```gradle lintOptions { disable 'BrokenIterator' } ``` In Android projects this should be nested inside an `android { }` block. * For manual invocations of `lint`, using the `--ignore` flag: ``` $ lint --ignore BrokenIterator ...` ``` * Last, but not least, using baselines, as discussed [here](https://googlesamples.github.io/android-custom-lint-rules/usage/baselines.md.html).