Как выполнить итерацию и изменение наборов Java?

Скажем, у меня есть набор целых чисел, и я хочу увеличивать каждое целое число в наборе. Как мне это сделать?

Мне разрешено добавлять и удалять элементы из набора при его итерации?

Нужно ли мне создать новый набор, который я бы "скопировал и модифицировал" элементы, в то время как я повторяю исходный набор?

EDIT: что, если элементы набора являются неизменяемыми?

Ответ 1

Вы можете безопасно удалить из набора во время итерации объект Iterator; пытаясь изменить набор через свой API, в то время как итерация приведет к поломке итератора. класс Set предоставляет итератор через getIterator().

однако объекты Integer неизменяемы; моя стратегия состояла в том, чтобы выполнить итерацию по множеству и для каждого Integer i, добавить я + 1 в некоторый новый временный набор. Когда вы закончите итерирование, удалите все элементы из исходного набора и добавьте все элементы нового временного набора.

Set<Integer> s; //contains your Integers
...
Set<Integer> temp = new Set<Integer>();
for(Integer i : s)
    temp.add(i+1);
s.clear();
s.addAll(temp);

Ответ 2

Вы можете делать то, что хотите, если используете объект итератора для перемещения элементов в своем наборе. Вы можете удалить их на ходу, и все в порядке. Однако удаление их в цикле for ( "стандартное" ) для каждого типа) вызовет у вас проблемы:

Set<Integer> set = new TreeSet<Integer>();
    set.add(1);
    set.add(2);
    set.add(3);

    //good way:
    Iterator<Integer> iterator = set.iterator();
    while(iterator.hasNext()) {
        Integer setElement = iterator.next();
        if(setElement==2) {
            iterator.remove();
        }
    }

    //bad way:
    for(Integer setElement:set) {
        if(setElement==2) {
            //might work or might throw exception, Java calls it indefined behaviour:
            set.remove(setElement);
        } 
    }

В соответствии с комментарием @mrgloom, вот более подробная информация о том, почему описанный выше "плохой" способ, ну... плохо:

Не вдаваясь в слишком подробные сведения о том, как Java реализует это, на высоком уровне мы можем сказать, что "плохой" способ плохой, потому что он четко оговорен как таковой в документах Java:

https://docs.oracle.com/javase/8/docs/api/java/util/ConcurrentModificationException.html

предусматривают, среди прочего, что (внимание мое):

" Например, для одного потока обычно не разрешено измените коллекцию, в то время как другой поток выполняет итерацию по ней. В вообще, результаты итерации undefined при этих обстоятельства. Некоторые реализации Итератора (в том числе всех реализации универсальной коллекции, предоставляемых JRE) может выбрать исключение этого исключения, если это поведение обнаружено" (...)

" Обратите внимание, что это исключение не всегда указывает, что объект был одновременно изменен другим потоком. Если один thread выдает последовательность вызовов метода, которая нарушает договор объекта, объект может исключить это исключение.Например, если поток изменяет коллекцию непосредственно, пока она итерация по сборке с помощью отказоустойчивого итератора, итератор будет исключать это исключение".

Чтобы перейти к деталям: объект, который может использоваться в цикле forEach, должен реализовать интерфейс java.lang.Iterable(javadoc здесь). Это создает Iterator (через метод "Итератор", найденный в этом интерфейсе), который создается по требованию и будет содержать внутреннюю ссылку к объекту Iterable, из которого он был создан. Однако, когда объект Iterable используется в цикле forEach, экземпляр этого итератора скрыт для пользователя (вы никак не можете получить к нему доступ).

Это в сочетании с тем фактом, что итератор довольно сдержанный, т.е. для того, чтобы сделать свою магию и иметь последовательные ответы для своих методов "next" и "hasNext", для этого требуется, чтобы объект подстановки не изменялся чем-то другим, сам итератор во время его итерации делает это так, что он будет генерировать исключение, как только он обнаружит, что что-то изменилось в фоновом объекте, когда оно итерирует его.

Java вызывает эту "неудачную" итерацию: т.е. есть некоторые действия, обычно те, которые изменяют экземпляр Iterable (в то время как Итератор выполняет итерацию по нему). "Неудачная" часть понятия "fail-fast" относится к способности Итератора обнаруживать, когда происходят такие "неудачные" действия. "Быстрая" часть "fail-fast" (и, на мой взгляд, должна быть названа "best-effort-fast" ), завершит итерацию через ConcurrentModificationException, как только она сможет обнаружить, что действие "fail" произойдет.

Ответ 3

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

private Map<String, String> JSONtoMAP(String jsonString) {

    JSONObject json = new JSONObject(jsonString);
    Map<String, String> outMap = new HashMap<String, String>();

    for (String curKey : (Set<String>) json.keySet()) {
        outMap.put(curKey, json.getString(curKey));
    }

    return outMap;

}

Ответ 4

Вы можете создать изменчивую оболочку примитивного int и создать набор из них:

class MutableInteger
{
    private int value;
    public int getValue()
    {
        return value;
    }
    public void setValue(int value)
    {
        this.value = value;
    }
}

class Test
{
    public static void main(String[] args)
    {
        Set<MutableInteger> mySet = new HashSet<MutableInteger>();
        // populate the set
        // ....

        for (MutableInteger integer: mySet)
        {
            integer.setValue(integer.getValue() + 1);
        }
    }
}

Конечно, если вы используете HashSet, вы должны реализовать хеш-метод equals в своем MutableInteger, но вне сферы действия этого ответа.

Ответ 5

Во-первых, я считаю, что попытка сделать несколько вещей одновременно является плохой практикой в ​​целом, и я предлагаю вам подумать над тем, чего вы пытаетесь достичь.

Он служит хорошим теоретическим вопросом, хотя и из того, что я собираю, реализация CopyOnWriteArraySet реализации интерфейса java.util.Set удовлетворяет вашим довольно особым требованиям.

http://download.oracle.com/javase/1,5.0/docs/api/java/util/concurrent/CopyOnWriteArraySet.html