Use flattenedToList/flattenedToSet instead of generic expand().
It is faster.
[expand: 102]
[flattened: 131]
[flattened2: 37]
import 'package:collection/collection.dart';
main() {
const outerLength = 100;
const innerLength = 10;
final listOfLists = List.generate(outerLength, (i) {
return List.generate(innerLength, (j) => i * innerLength + j);
});
for (var i = 0; i < 10; i++) {
f(listOfLists);
}
}
void f(List<List<int>> listOfLists) {
const repeatCount = 10000;
{
final timer = Stopwatch()..start();
for (var i = 0; i < repeatCount; i++) {
listOfLists.expand((e) => e).toList();
}
print('[expand: ${timer.elapsedMilliseconds}]');
}
{
final timer = Stopwatch()..start();
for (var i = 0; i < repeatCount; i++) {
listOfLists.flattened.toList();
}
print('[flattened: ${timer.elapsedMilliseconds}]');
}
{
final timer = Stopwatch()..start();
for (var i = 0; i < repeatCount; i++) {
listOfLists.flattened2.toList();
}
print('[flattened2: ${timer.elapsedMilliseconds}]');
}
}
extension ListListExtensions<T> on List<List<T>> {
Iterable<T> get flattened2 {
return [
for (final elements in this) ...elements,
];
}
}
Change-Id: I0bf9dc0c8735fe62aab69cbce276254e2db110f9
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/345202
Reviewed-by: Jacob Richman <jacobr@google.com>
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Reviewed-by: Phil Quitslund <pquitslund@google.com>
Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
This commit is contained in:
committed by
Commit Queue
parent
882e883be2
commit
8d3b6ce54c
@@ -8,6 +8,7 @@ import 'package:analysis_server/lsp_protocol/protocol.dart';
|
||||
import 'package:analysis_server/src/lsp/handlers/code_actions/abstract_code_actions_producer.dart';
|
||||
import 'package:analysis_server/src/lsp/mapping.dart';
|
||||
import 'package:analyzer/src/dart/analysis/driver.dart';
|
||||
import 'package:analyzer/src/utilities/extensions/collection.dart';
|
||||
import 'package:analyzer_plugin/protocol/protocol.dart' as plugin;
|
||||
import 'package:analyzer_plugin/protocol/protocol_generated.dart' as plugin;
|
||||
import 'package:analyzer_plugin/src/protocol/protocol_internal.dart' as plugin;
|
||||
@@ -60,8 +61,7 @@ class PluginCodeActionsProducer extends AbstractCodeActionsProducer {
|
||||
.map((response) => plugin.EditGetFixesResult.fromResponse(response))
|
||||
.expand((response) => response.fixes)
|
||||
.map(_convertFixes)
|
||||
.expand((fix) => fix)
|
||||
.toList();
|
||||
.flattenedToList;
|
||||
}
|
||||
|
||||
@override
|
||||
|
||||
@@ -13,6 +13,7 @@ import 'package:analysis_server/src/lsp/registration/feature_registration.dart';
|
||||
import 'package:analysis_server/src/lsp/semantic_tokens/encoder.dart';
|
||||
import 'package:analysis_server/src/lsp/semantic_tokens/legend.dart';
|
||||
import 'package:analyzer/source/source_range.dart';
|
||||
import 'package:analyzer/src/utilities/extensions/collection.dart';
|
||||
import 'package:analyzer_plugin/protocol/protocol_common.dart';
|
||||
|
||||
typedef StaticOptions
|
||||
@@ -65,8 +66,7 @@ abstract class AbstractSemanticTokensHandler<T>
|
||||
|
||||
return toSourceRangeNullable(lineInfo, range).mapResult((range) async {
|
||||
final serverTokens = await getServerResult(path, range);
|
||||
final pluginHighlightRegions =
|
||||
getPluginResults(path).expand((results) => results).toList();
|
||||
final pluginHighlightRegions = getPluginResults(path).flattenedToList;
|
||||
|
||||
if (token.isCancellationRequested) {
|
||||
return cancelled();
|
||||
|
||||
@@ -27,6 +27,7 @@ import 'package:analyzer/source/source_range.dart' as server;
|
||||
import 'package:analyzer/src/dart/analysis/search.dart' as server
|
||||
show DeclarationKind;
|
||||
import 'package:analyzer/src/error/codes.dart';
|
||||
import 'package:analyzer/src/utilities/extensions/collection.dart';
|
||||
import 'package:analyzer_plugin/protocol/protocol_common.dart' as plugin;
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:path/path.dart' as path;
|
||||
@@ -1165,8 +1166,7 @@ List<lsp.DocumentHighlight> toHighlights(
|
||||
.map((occurrence) => occurrence.offsets.map((offset) =>
|
||||
lsp.DocumentHighlight(
|
||||
range: toRange(lineInfo, offset, occurrence.length))))
|
||||
.expand((occurrences) => occurrences)
|
||||
.toSet()
|
||||
.flattenedToSet
|
||||
.toList();
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ import 'dart:math' as math;
|
||||
import 'package:analysis_server/lsp_protocol/protocol.dart';
|
||||
import 'package:analysis_server/src/lsp/constants.dart';
|
||||
import 'package:analysis_server/src/lsp/semantic_tokens/mapping.dart';
|
||||
import 'package:analyzer/src/utilities/extensions/collection.dart';
|
||||
import 'package:meta/meta.dart';
|
||||
|
||||
final semanticTokenLegend = SemanticTokenLegendLookup();
|
||||
@@ -33,8 +34,8 @@ class SemanticTokenLegendLookup {
|
||||
_usedTokenTypes = Set.of(highlightRegionTokenTypes.values
|
||||
.followedBy(CustomSemanticTokenTypes.values))
|
||||
.toList();
|
||||
_usedTokenModifiers = Set.of(highlightRegionTokenModifiers.values
|
||||
.expand((v) => v)
|
||||
_usedTokenModifiers = Set.of(highlightRegionTokenModifiers
|
||||
.values.flattenedToList
|
||||
.followedBy(CustomSemanticTokenModifiers.values))
|
||||
.toList();
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ import 'package:analyzer/dart/ast/ast.dart';
|
||||
import 'package:analyzer/exception/exception.dart';
|
||||
import 'package:analyzer/source/line_info.dart';
|
||||
import 'package:analyzer/src/generated/source.dart';
|
||||
import 'package:analyzer/src/utilities/extensions/collection.dart';
|
||||
|
||||
Future<void> scheduleImplementedNotification(
|
||||
LegacyAnalysisServer server, Iterable<String> files) async {
|
||||
@@ -42,8 +43,7 @@ void sendAnalysisNotificationAnalyzedFiles(LegacyAnalysisServer server) {
|
||||
_sendNotification(server, () {
|
||||
var analyzedFiles = server.driverMap.values
|
||||
.map((driver) => driver.knownFiles)
|
||||
.expand((files) => files)
|
||||
.toSet();
|
||||
.flattenedToSet;
|
||||
|
||||
// Exclude *.yaml files because IDEA Dart plugin attempts to index
|
||||
// all the files in folders which contain analyzed files.
|
||||
|
||||
+3
-3
@@ -22,6 +22,7 @@ import 'package:analysis_server/src/services/correction/fix/data_driven/variable
|
||||
import 'package:analysis_server/src/services/refactoring/framework/formal_parameter.dart';
|
||||
import 'package:analyzer/error/listener.dart';
|
||||
import 'package:analyzer/src/util/yaml.dart';
|
||||
import 'package:analyzer/src/utilities/extensions/collection.dart';
|
||||
import 'package:analyzer/src/utilities/extensions/string.dart';
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:yaml/yaml.dart';
|
||||
@@ -350,9 +351,8 @@ class TransformSetParser {
|
||||
var firstEntry = entries.firstOrNull;
|
||||
if (firstEntry == null) {
|
||||
if (required) {
|
||||
var validKeysList = translators.keys
|
||||
.expand((keys) => keys)
|
||||
.quotedAndCommaSeparatedWithOr;
|
||||
var validKeysList =
|
||||
translators.keys.flattenedToList.quotedAndCommaSeparatedWithOr;
|
||||
_reportError(TransformSetErrorCode.missingOneOfMultipleKeys, errorNode,
|
||||
[validKeysList]);
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import 'package:analyzer/dart/ast/ast.dart';
|
||||
import 'package:analyzer/dart/ast/visitor.dart';
|
||||
import 'package:analyzer/dart/element/element.dart';
|
||||
import 'package:analyzer/source/source_range.dart';
|
||||
import 'package:analyzer/src/utilities/extensions/collection.dart';
|
||||
|
||||
/// A utility class used to analyze a library from which some set of
|
||||
/// declarations are being moved in order to compute the set of changes needed
|
||||
@@ -298,8 +299,7 @@ class _ReferenceFinder extends RecursiveAstVisitor<void> {
|
||||
// Extensions can be used without a prefix, so we can use any import that
|
||||
// brings in the extension.
|
||||
if (import == null && prefix == null && element is ExtensionElement) {
|
||||
import = _importsByPrefix.values
|
||||
.expand((imports) => imports)
|
||||
import = _importsByPrefix.values.flattenedToList
|
||||
.where((import) =>
|
||||
// Because we don't know what prefix we're looking for (any is
|
||||
// allowed), use the imports own prefix when checking for the
|
||||
|
||||
@@ -51,6 +51,7 @@ import 'package:analyzer/src/summary2/macro.dart';
|
||||
import 'package:analyzer/src/summary2/package_bundle_format.dart';
|
||||
import 'package:analyzer/src/util/file_paths.dart' as file_paths;
|
||||
import 'package:analyzer/src/util/performance/operation_performance.dart';
|
||||
import 'package:analyzer/src/utilities/extensions/collection.dart';
|
||||
import 'package:analyzer/src/utilities/uri_cache.dart';
|
||||
|
||||
/// This class computes [AnalysisResult]s for Dart files.
|
||||
@@ -2558,7 +2559,7 @@ class _DiscoverAvailableFilesTask {
|
||||
// Discover files in package/lib folders.
|
||||
var packageMap = driver._sourceFactory.packageMap;
|
||||
if (packageMap != null) {
|
||||
folderIterator = packageMap.values.expand((f) => f).iterator;
|
||||
folderIterator = packageMap.values.flattenedToList.iterator;
|
||||
} else {
|
||||
folderIterator = <Folder>[].iterator;
|
||||
}
|
||||
|
||||
@@ -149,7 +149,7 @@ class _LibraryNode extends graph.Node<_LibraryNode> {
|
||||
.whereType<LibraryExportWithFile>()
|
||||
.map((export) => export.exportedLibrary),
|
||||
])
|
||||
.expand((libraries) => libraries)
|
||||
.flattenedToList
|
||||
.whereNotNull()
|
||||
.toSet();
|
||||
|
||||
|
||||
@@ -27,6 +27,7 @@ import 'package:analyzer/src/dart/element/type_schema.dart';
|
||||
import 'package:analyzer/src/dart/element/type_system.dart';
|
||||
import 'package:analyzer/src/error/codes.dart'
|
||||
show CompileTimeErrorCode, WarningCode;
|
||||
import 'package:analyzer/src/utilities/extensions/collection.dart';
|
||||
import 'package:meta/meta.dart';
|
||||
|
||||
/// Tracks upper and lower type bounds for a set of type parameters.
|
||||
@@ -491,7 +492,7 @@ class GenericInferrer {
|
||||
.values
|
||||
.where((l) =>
|
||||
l.every((c) => c.isSatisfiedBy(_typeSystem, inferred)) == expected)
|
||||
.expand((i) => i);
|
||||
.flattenedToList;
|
||||
|
||||
String unsatisfied = _formatConstraints(isSatisfied(false));
|
||||
String satisfied = _formatConstraints(isSatisfied(true));
|
||||
|
||||
@@ -68,6 +68,7 @@ import 'package:analyzer/src/fasta/doc_comment_builder.dart';
|
||||
import 'package:analyzer/src/fasta/error_converter.dart';
|
||||
import 'package:analyzer/src/generated/utilities_dart.dart';
|
||||
import 'package:analyzer/src/summary2/ast_binary_tokens.dart';
|
||||
import 'package:analyzer/src/utilities/extensions/collection.dart';
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:meta/meta.dart';
|
||||
import 'package:pub_semver/pub_semver.dart';
|
||||
@@ -3071,7 +3072,7 @@ class AstBuilder extends StackListener {
|
||||
debugEvent("SwitchBlock");
|
||||
|
||||
var membersList = popTypedList2<List<SwitchMemberImpl>>(caseCount);
|
||||
var members = membersList.expand((members) => members).toList();
|
||||
var members = membersList.flattenedToList;
|
||||
|
||||
Set<String> labels = <String>{};
|
||||
for (var member in members) {
|
||||
|
||||
@@ -460,7 +460,7 @@ class LibraryBuilder {
|
||||
}
|
||||
|
||||
final augmentationCode = macroApplier.buildAugmentationLibraryCode(
|
||||
_macroResults.expand((e) => e).toList(),
|
||||
_macroResults.flattenedToList,
|
||||
);
|
||||
if (augmentationCode == null) {
|
||||
return;
|
||||
|
||||
@@ -19,6 +19,26 @@ extension IterableExtension<E> on Iterable<E> {
|
||||
}
|
||||
}
|
||||
|
||||
extension IterableIterableExtension<T> on Iterable<Iterable<T>> {
|
||||
/// Elements of each iterable in this iterable.
|
||||
///
|
||||
/// At the moment of writing, this method is `2.75` times faster than
|
||||
/// `expand((e) => e)`, and `3.5` faster than `flattened` from
|
||||
/// `package:collection`.
|
||||
List<T> get flattenedToList {
|
||||
return [
|
||||
for (final elements in this) ...elements,
|
||||
];
|
||||
}
|
||||
|
||||
/// Elements of each iterable in this iterable.
|
||||
Set<T> get flattenedToSet {
|
||||
return {
|
||||
for (final elements in this) ...elements,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
extension IterableMapEntryExtension<K, V> on Iterable<MapEntry<K, V>> {
|
||||
Map<K, V> get mapFromEntries => Map.fromEntries(this);
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import 'package:analyzer/dart/element/type.dart';
|
||||
import 'package:analyzer/src/dart/element/element.dart';
|
||||
import 'package:analyzer/src/dart/element/extensions.dart';
|
||||
import 'package:analyzer/src/dart/element/inheritance_manager3.dart';
|
||||
import 'package:analyzer/src/utilities/extensions/collection.dart';
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:test/test.dart';
|
||||
import 'package:test_reflective_loader/test_reflective_loader.dart';
|
||||
@@ -3022,7 +3023,7 @@ class _InterfacePrinter {
|
||||
String name,
|
||||
Map<Name, List<ExecutableElement>> map,
|
||||
) {
|
||||
final isEmpty = map.values.expand((elements) => elements).where((element) {
|
||||
final isEmpty = map.values.flattenedToList.where((element) {
|
||||
if (_configuration.withObjectMembers) return true;
|
||||
return !element.isObjectMember;
|
||||
}).isEmpty;
|
||||
|
||||
@@ -9,6 +9,7 @@ import 'package:test_reflective_loader/test_reflective_loader.dart';
|
||||
main() {
|
||||
defineReflectiveSuite(() {
|
||||
defineReflectiveTests(IterableExtensionTest);
|
||||
defineReflectiveTests(IterableIterableExtensionTest);
|
||||
defineReflectiveTests(IterableMapEntryExtensionTest);
|
||||
defineReflectiveTests(ListExtensionTest);
|
||||
});
|
||||
@@ -21,6 +22,31 @@ class IterableExtensionTest {
|
||||
}
|
||||
}
|
||||
|
||||
@reflectiveTest
|
||||
class IterableIterableExtensionTest {
|
||||
test_flattenedToList() {
|
||||
expect(
|
||||
[
|
||||
[0],
|
||||
[1, 2],
|
||||
[3, 3]
|
||||
].flattenedToList,
|
||||
[0, 1, 2, 3, 3],
|
||||
);
|
||||
}
|
||||
|
||||
test_flattenedToSet() {
|
||||
expect(
|
||||
[
|
||||
[0, 0],
|
||||
[1, 2, 1],
|
||||
[3, 3]
|
||||
].flattenedToSet,
|
||||
{0, 1, 2, 3},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@reflectiveTest
|
||||
class IterableMapEntryExtensionTest {
|
||||
test_mapFromEntries() {
|
||||
|
||||
Reference in New Issue
Block a user