For-Each и Указатели в Java [дублировать]
Вопрос
На этот вопрос уже есть ответ здесь:
Хорошо, итак, я пытаюсь выполнить итерацию по ArrayList и удалить определенный элемент.Однако у меня возникают некоторые проблемы с использованием структуры For-Each like.Когда я запускаю следующий код:
ArrayList<String> arr = new ArrayList<String>();
//... fill with some values (doesn't really matter)
for(String t : arr)
{
t = " some other value "; //hoping this would change the actual array
}
for(String t : arr)
{
System.out.println(t); //however, I still get the same array here
}
Мой вопрос в том, как я могу сделать 't' указателем на 'arr', чтобы я мог изменять значения в цикле для каждого?Я знаю, что мог бы перебирать ArrayList, используя другую структуру, но эта выглядит такой чистой и читаемой, что было бы просто неплохо иметь возможность сделать 't' указателем.
Все комментарии приветствуются!Даже если ты скажешь, что я должен просто смириться с этим и использовать другую конструкцию.
Решение
Я думаю, что лучшим подходом может быть использование цикла for .
ArrayList<String> arr = new ArrayList<String>();
for (int i = 0; i < arr.size(); i++) {
String t = arr.get(i);
if (// your condition is met) {
arr.set(i, "your new value");
}
}
Другие советы
Проблема в том, что вы пытаетесь изменить область действия цикла ссылка t
чтобы он указывал на новый экземпляр String.Это не сработает.Он не ссылается на фактическую запись в arraylist .Вам нужно изменить фактический значение из ссылки.Если String
был изменяемым и предоставлял фиктивный set()
метод для этого, вы могли бы теоретически сделать
for (String t : arr) {
t.set("some other value");
}
или так, но это невозможно, поскольку оно неизменяемо.Лучше получить дескриптор точки входа в самом массиве, используя обычный for
петля:
for (int i = 0; i < arr.size(); i++) {
arr.set(i, "some other value");
}
Если вы настаиваете на использовании расширенного for
цикл, затем вам нужно заменить String
Автор: StringBuilder
, который является изменяемым:
for (StringBuilder t : arr) {
t.delete(0, t.length()).append("some other value");
}
Помните, Java - это передача по значению, а не передача по ссылке.
For-each не дает вам указателя индекса, поэтому вы просто не можете использовать его для изменения неизменяемого значения.
Либо используйте цикл for с индексом, либо используйте изменяемый тип (например, StringBuffer, а не String)
Массив объектов (например, строк) в Java представляет собой непрерывный блок, содержащий упорядоченный ряд ссылок.Итак, когда у вас есть массив из 4 строк, то на самом деле у вас есть 4 ссылки, хранящиеся В массиве, и 4 строковых объекта, которые находятся за пределами массива, но на которые ссылаются его 4 элемента.
Что делает конструкция for-each в Java, так это создает локальную переменную и для каждой итерации копирует в эту локальную переменную ссылку из ячейки массива, которая соответствует этой итерации.Когда вы устанавливаете переменную цикла (t = " some other value"
) вы помещаете ссылку на новую строку, "some other value"
, в локальную переменную t, а не в массив.
Это контрастирует с некоторыми другими языками (например, Perl), где переменная цикла действует как псевдоним самого элемента массива / списка.
Ваш код переписывается компилятором примерно так:
ArrayList<String> arr = new ArrayList<String>();
//... fill with some values (doesn't really matter)
for (final Iterator <String> i = arr.iterator(); i.hasNext();) {
String t;
t = i.next();
t = " some other value "; // just changes where t is pointing
}
Чтобы делать то, что вы хотите, вам пришлось бы написать цикл for следующим образом:
for (final ListIterator<String> i = arr.iterator(); i.hasNext();) {
final String t;
t = i.next();
i.set("some other value");
}
У Iterator нет метода set, есть только у ListIterator .
По сути, вы хотите удалить строку t из списка arr.Просто выполните arr.remove(t), и все может быть готово. Но вы не можете сделать это, выполняя итерацию по одному и тому же списку.Вы получите исключение, если попытаетесь изменить список таким образом.
У вас есть два варианта:
- клонируйте свой список, выполните итерацию по клонированию и удалите "конкретную" строку из исходного списка
- создайте список для кандидатов на удаление, добавьте все "определенные" строки в этот список и, после перебора исходного списка, выполните перебор корзины для мусора и удалите все, что вы собрали здесь, из исходного списка.
Вариант 1 - самый простой, клон может быть сделан следующим образом:
List<String> clone = new ArrayList<String>(arr);
Похоже, вы неправильно понимаете, как объекты / ссылки работают в Java, что является довольно фундаментальным для эффективного использования языка.Однако этот код здесь должен делать то, что вы хотите (приносим извинения за отсутствие объяснений):
ArrayList<String> arr = new ArrayList<String>();
//... fill with some values (doesn't really matter)
for(int i = 0; i < arr.size(); i++)
{
arr.set(i, " some other value "); // change the contents of the array
}
for(String t : arr)
{
System.out.println(t);
}
Я полагаю, это не имеет отношения к неизменяемому или изменяемому.
t = " some other value "; //hoping this would change the actual array
t не содержит ссылки на фактический объект.Java копирует значение из arraylist и помещает это значение в t, чтобы значение списка массива не влияло.
HTH
На этот вопрос был дан хороший ответ.И все же вот мое предложение.Внутренний цикл var t виден только там.Это не будет видно за пределами цикла.Вы могли бы сделать t.set()
если бы это было не так String
.
Используйте StringBuffer
вместо простых строк.Таким образом, строка внутри является изменяемой.
Строки являются неизменяемыми.Если бы у вас был изменяемый тип, такой как StringBuilder / Buffer , вы могли бы изменить строку в своей итерации.У тебя есть рекомендации, не забывай.