#dart
#dart
Вопрос:
Я выполняю вложенную итерацию по двум спискам, в которых я заполняю некоторые строковые буферы, например:
var int_list = [1, 2, 3];
var letters_list = ['a', 'b', 'c'];
var row_strings = List.filled(3, StringBuffer());
var single_buffer = StringBuffer();
int_list.asMap().forEach((int_index, column) {
letters_list.asMap().forEach((letter_index, letter) {
// debug the writing operation
print('writing $letter_index - $letter');
row_strings[letter_index].write(letter);
// try a buffer not in a list as a test
if (letter_index == 0) {
single_buffer.write(letter);
}
});
});
print(single_buffer);
print(row_strings);
Я ожидаю, что в списке строковых буферов буфер 0 получит все ‘a’, буфер 1 получит все ‘b’, а буфер 3 — ‘c’.
Вывод отладки подтверждает, что операция записи выполняется правильно:
writing 0 - a
writing 1 - b
writing 2 - c
writing 0 - a
writing 1 - b
writing 2 - c
writing 0 - a
writing 1 - b
writing 2 - c
и буфер с одной строкой получает правильный вывод:
aaa
Но вывод списка таков:
[abcabcabc, abcabcabc, abcabcabc]
Что здесь происходит? Кажется, что происходит какое-то странное поведение, когда строковые буферы находятся в списке.
Ответ №1:
Ваша проблема в этой строке:
var row_strings = List.filled(3, StringBuffer());
Этот конструктор задокументирован как:
List.filled(int length, E fill, {bool growable: false})
Создает список заданной длины с
fill
каждой позицией.
https://api.dart.dev/stable/2.10.5/dart-core/List/List.filled.html
Итак, что вы делаете, это создаете один StringBuffer
экземпляр и используете его для каждой позиции в вашем row_strings
списке.
Что вы действительно хотите, так это создать новый StringBuffer
для каждой позиции в списке. List.generate
Для этого вам нужно использовать:
List.generate(длина int, генератор E(индекс int), {bool growable: true})
Генерирует список значений.
Создает список с позициями длины и заполняет его значениями, созданными путем вызова генератора для каждого индекса в диапазоне 0 .. длина — 1 в порядке возрастания.
https://api.dart.dev/stable/2.10.5/dart-core/List/List.generate.html
Используя это, мы получаем:
final row_strings = List.generate(3, (_) => StringBuffer());
Нам не нужен index
аргумент в нашем генераторе, поэтому я только что вызвал его _
. Функция (_) => StringBuffer()
будет выполнена для каждой позиции в списке и сохранена. Поскольку функция out возвращает новый экземпляр StringBuffer
при каждом ее выполнении, в итоге мы получим список из 3 отдельных StringBuffer
экземпляров.
Комментарии:
1. Спасибо за объяснение!! На самом деле у меня было такое же неправильное использование filled() в других частях моего кода, но я еще не видел ошибок из них!
2. Если я это сделаю
var thislist = List.filled(2, 'string');
, то почему строки — это разные вещи, даже если строка тоже создается как объект?3. @joachim
String
неизменяемы, поэтому вы не можете изменить состояние самого объекта. Каждая операция, приString
которой выполняется манипулирование состоянием, создаст новыйString
объект. То же самое, напримерint
, иdouble
. Поэтому не проблема, что мы повторно используем одно и то жеString
для каждого элемента в списке, поскольку мы знаем, что если мы захотим изменить значение, мы в конечном итоге создадим новый объект. То же самое нельзя сказать оStringBuffer
том, что содержит внутреннее состояние.4. Еще раз спасибо за объяснение 🙂
5. Также можно использовать литерал списка
[for (var i = 0; i < 3; i ) StringBuffer()]
.