Добавление элементов к набору во время повторения

Мало того, что процессоры x86 способны считывать и записывать один байт, на это способны все современные процессоры общего назначения. Что еще более важно, большинство современных процессоров (включая x86, ARM, MIPS, PowerPC и SPARC) способны атомарно считывать и записывать отдельные байты.

Я не уверен, о чем говорил Страуструп. Раньше было несколько машин с адресацией слов, которые не были способны к 8-битной адресации байтов, например Cray, и, как сказал Питер Кордес, ранние процессоры Alpha не поддерживали загрузку и хранение байтов, но сегодня единственные процессоры, неспособные к байту, сегодня. нагрузки и хранилища - это определенные DSP, используемые в нишевых приложениях. Даже если предположить, что он имеет в виду, что большинство современных процессоров не имеют атомарной байтовой нагрузки и сохраняют данные, это не относится к большинству процессоров.

Тем не менее, простые атомарные загрузки и хранилища не очень полезны в многопоточном программировании. Вам также обычно нужны гарантии упорядочения и способ сделать операции чтения-изменения-записи атомарными. Другое соображение заключается в том, что, хотя процессор a может иметь инструкции загрузки и хранения байтов, компилятору не требуется их использовать. Например, компилятор все еще может генерировать код, описанный Страуструпом, загружая как b, так и c, используя инструкцию загрузки одного слова в качестве оптимизации.

Поэтому, хотя вам нужна четко определенная модель памяти, если только так компилятор вынужден генерировать код, который вы ожидаете, проблема не в том, что современные процессоры не способны загружать или хранить что-то меньшее, чем слово.

76
задан Dave Newton 14 October 2018 в 04:20
поделиться

9 ответов

Как насчет создания очереди с элементами, которые вы хотите перебирать; когда вы хотите добавить элементы, ставьте их в очередь в конце очереди и продолжайте удалять элементы, пока очередь не станет пустой. Вот как обычно работает поиск в ширину.

60
ответ дан 24 November 2019 в 11:19
поделиться

В общем это небезопасно, хотя для некоторых коллекций может быть. Очевидная альтернатива - использовать какой-то цикл for. Но вы не сказали, какую коллекцию используете, так что это может быть, а может и нет.

-1
ответ дан 24 November 2019 в 11:19
поделиться

ИМХО, более безопасным способом было бы создать новую коллекцию, перебирать данную коллекцию, добавляя каждый элемент в новую коллекцию, а также добавляя дополнительные элементы в новую коллекцию, наконец возвращение новой коллекции.

0
ответ дан 24 November 2019 в 11:19
поделиться

Я предпочитаю обрабатывать коллекции функционально, а не изменять их на месте. Это позволяет полностью избежать подобных проблем, а также проблем с псевдонимом и других сложных источников ошибок.

Я бы реализовал это так:

List<Thing> expand(List<Thing> inputs) {
    List<Thing> expanded = new ArrayList<Thing>();

    for (Thing thing : inputs) {
        expanded.add(thing);
        if (needsSomeMoreThings(thing)) {
            addMoreThingsTo(expanded);
        }
    }

    return expanded;
}
0
ответ дан 24 November 2019 в 11:19
поделиться

Использование итераторов ... нет, я так не думаю. Вам нужно будет собрать что-то вроде этого:

    Collection< String > collection = new ArrayList< String >( Arrays.asList( "foo", "bar", "baz" ) );
    int i = 0;
    while ( i < collection.size() ) {

        String curItem = collection.toArray( new String[ collection.size() ] )[ i ];
        if ( curItem.equals( "foo" ) ) {
            collection.add( "added-item-1" );
        }
        if ( curItem.equals( "added-item-1" ) ) {
            collection.add( "added-item-2" );
        }

        i++;
    }

    System.out.println( collection );

Какие результаты:
[foo, bar, baz, добавлен-элемент-1, добавлен-элемент-2]

1
ответ дан 24 November 2019 в 11:19
поделиться

Вы также можете посмотреть некоторые из более специализированных типов, например ListIterator , NavigableSet и (если вас интересуют карты) NavigableMap .

8
ответ дан 24 November 2019 в 11:19
поделиться

Здесь есть две проблемы:

Первая проблема заключается в добавлении в коллекцию после возврата Iterator . Как уже упоминалось, не существует определенного поведения при изменении базовой Collection , как указано в документации для Iterator.remove :

... Поведение итератора является не указано, если основной коллекция изменяется, пока итерация выполняется любым способом кроме вызова этого метода.

Вторая проблема заключается в том, что даже если можно было бы получить Iterator и затем вернуться к тому же элементу, в котором находился Iterator , нет гарантия порядка итераций, как указано в документации метода Collection.iterator :

... Нет никаких гарантий относительно порядок, в котором элементы возвращается (если эта коллекция не является экземпляр некоторого класса, который предоставляет гарантия).

Например, у нас есть список [1, 2, 3, 4] .

Допустим, 5 был добавлен, когда Итератор находился на 3 , и каким-то образом мы получаем Итератор , который может возобновить итерацию с 4 . Однако нет гарантии, что 5 появится после 4 . Порядок итераций может быть [5, 1, 2, 3, 4] - тогда итератор все равно пропустит элемент 5 .

Поскольку нет гарантии для поведение, нельзя предполагать, что что-то произойдет определенным образом.

Одной из альтернатив может быть отдельная Коллекция , к которой могут быть добавлены вновь созданные элементы, а затем итерация по этим элементам:

Collection<String> list = Arrays.asList(new String[]{"Hello", "World!"});
Collection<String> additionalList = new ArrayList<String>();

for (String s : list) {
    // Found a need to add a new element to iterate over,
    // so add it to another list that will be iterated later:
    additionalList.add(s);
}

for (String s : additionalList) {
    // Iterate over the elements that needs to be iterated over:
    System.out.println(s);
}

Править

Доработав ответ Avi , можно поставить в очередь элементы, которые мы хотим перебрать, и удалить элементы, пока в очереди есть элементы. Это позволит «перебирать» новые элементы в дополнение к исходным.

Давайте посмотрим, как это будет работать.

Концептуально, если у нас есть следующие элементы в очереди:

[1, 2, 3, 4]

И, когда мы удаляем 1 , мы решаем добавить 42 , очередь будет следующей :

[2, 3, 4, 42]

Поскольку очередь представляет собой структуру данных FIFO (первый пришел - первый вышел), такой порядок является типичным. (Как отмечено в документации к интерфейсу Queue , это не является необходимостью в Queue . Возьмем случай PriorityQueue , который упорядочивает элементы по их естественному упорядочение, так что это не FIFO.)

Ниже приведен пример использования LinkedList (который представляет собой Queue ), чтобы пройти все элементы вместе с добавленными дополнительными элементами во время удаления. Как и в примере выше, элемент 42 добавляется, когда элемент 2 удаляется:

Queue<Integer> queue = new LinkedList<Integer>();
queue.add(1);
queue.add(2);
queue.add(3);
queue.add(4);

while (!queue.isEmpty()) {
    Integer i = queue.remove();
    if (i == 2)
        queue.add(42);

    System.out.println(i);
}

Результат следующий:

1
2
3
4
42

Как и предполагалось, элемент 42 , который был добавлен, когда мы нажали . 2 появилось.

46
ответ дан 24 November 2019 в 11:19
поделиться

Помимо решения с использованием дополнительного списка и вызова addAll для вставки новых элементов после итерации (например, решение пользователя Nat), вы также можете использовать параллельные коллекции, такие как CopyOnWriteArrayList .

Метод итератора в стиле «моментальный снимок» использует ссылку на состояние массива в момент создания итератора. Этот массив никогда не изменяется в течение времени жизни итератора, поэтому вмешательство невозможно, и итератор гарантированно не вызовет ConcurrentModificationException.

С помощью этой специальной коллекции (обычно используемой для одновременного доступа) можно манипулировать базовым списком во время итерации по Это. Однако итератор не отразит изменения.

Это лучше, чем другое решение? Наверное, нет, я не

0
ответ дан 24 November 2019 в 11:19
поделиться

На самом деле это довольно легко. Просто подумайте оптимально. Я верю оптимальному способу:

for (int i=0; i<list.size(); i++) {
   Level obj = list.get(i);

   //Here execute yr code that may add / or may not add new element(s)
   //...

   i=list.indexOf(obj);
}

В следующем примере отлично работает в наиболее логическом случае - когда вам не нужно повторять добавленные новые элементы перед элементом итерации. Об добавленных элементах после элемента итерации - там вы можете не повторить их. В этом случае вы должны просто добавить / или расширить объект YR с флагом, который помечет их не имитировать их.

4
ответ дан 24 November 2019 в 11:19
поделиться
Другие вопросы по тегам:

Похожие вопросы: