1. 이터레이터가 생겨난 배경

당신은 이미 에 익숙합니다 HashSet. 강의를 읽는 것 이상으로 실제로 조사했다면 다음과 같은 질문을 했어야 합니다.

화면에 모든 HashSet 요소 목록을 어떻게 표시합니까? 결국 인터페이스에는 get()메서드 가 없습니다 set()!

그리고 HashSet이 한계는 혼자가 아닙니다. 이외에도 HashSet요소에 정의된 순서가 없기 때문에 인덱스로 요소를 검색할 수 없도록 하는 다른 많은 컬렉션이 있습니다.

수년 동안 프로그래머는 그래프 및 트리와 같은 복잡한 데이터 구조를 많이 발명했습니다. 또는 목록 목록.

많은 컨테이너는 새 요소가 추가되거나 기존 요소가 제거될 때 해당 요소의 순서를 변경합니다. 예를 들어, 목록은 특정 순서로 요소를 저장하고 새 요소가 추가되면 거의 항상 목록 중간에 삽입됩니다.

또한 요소를 저장하지만 고정된 순서로 저장하지 않는 컨테이너가 있는 상황도 있습니다.

이제 이러한 컬렉션의 모든 요소를 ​​배열이나 목록에 복사하려고 한다고 가정해 보겠습니다. 모든 요소를 ​​가져와야 합니다. 요소를 반복하는 순서는 중요하지 않습니다. 중요한 것은 동일한 요소를 두 번 이상 반복하지 않는 것입니다. 어떻게 하죠?


2. 컬렉션의 반복자

반복자는 위의 문제에 대한 해결책으로 제안되었습니다.

반복자는 컬렉션과 연결된 특수 개체로, 반복하지 않고 컬렉션의 모든 요소를 ​​순회하는 데 도움이 됩니다.

다음 코드를 사용하여 모든 컬렉션에 대한 반복자를 가져올 수 있습니다.

Iterator<Type> it = name.iterator();

여기서 name컬렉션 변수의 이름은 Type컬렉션 요소의 유형이고 iterator()컬렉션의 메서드 중 하나이며 it반복자 변수의 이름입니다.

반복자 객체에는 3가지 메서드가 있습니다.

방법 설명
Type next()
컬렉션의 다음 요소를 반환합니다.
boolean hasNext()
아직 통과하지 않은 요소가 있는지 확인합니다.
void remove()
컬렉션의 현재 요소를 제거합니다.

이러한 메서드는 Scanner 클래스의 nextInt)hasNextInt()메서드와 다소 유사합니다.

next()메서드는 반복자를 가져온 컬렉션의 다음 요소를 반환합니다.

메서드 hasNext()는 반복자가 아직 반환하지 않은 추가 요소가 컬렉션에 있는지 확인합니다.

다음은 a 의 모든 요소를 ​​표시하는 방법입니다 HashSet.

암호 노트
HashSet<String> set = new HashSet<String>();

set.add("Hallo");
set.add("Hello");
set.add("Hola");
set.add("Bonjour");
set.add("Ciao");
set.add("Namaste");

Iterator<String> it = set.iterator();
while (it.hasNext())
{
   String str = it.next();
   System.out.println(str);
}
HashSet요소를 저장하는 개체를 만듭니다 String.


변수 에 다양한 언어로 인사말을 추가합니다 set.




집합 에 대한 반복자 개체를 가져옵니다 set.
아직 요소가 있는 한

다음 요소를 가져옵니다.
요소를 화면에 표시합니다.


3. For-each루프

반복자의 주요 단점은 for루프를 사용하는 것보다 코드가 더 복잡해진다는 것입니다.

for비교를 위해 루프와 반복자를 사용하여 목록을 표시해 보겠습니다 .

반복자 for 루프
ArrayList<String> list = new ArrayList<String>();

Iterator<String> it = list.iterator();
while (it.hasNext())
{
   String str = it.next();
   System.out.println(str);
}
ArrayList<String> list = new ArrayList<String>();

for (int i = 0; i < list.size(); i++)
{
   String str = list.get(i);
   System.out.println(str);
}

예, using 루프의 요소를 순회하는 것이 훨씬 낫습니다 ArrayList. 모든 것이 더 짧아집니다.

그러나 Java 제작자는 다시 우리에게 약간의 설탕을 붓기로 결정했습니다. 운 좋게도 구문 설탕 이었습니다 .

그들은 Java에 새로운 종류의 루프를 제공하고 이를 for-each루프라고 불렀습니다. 일반적으로 다음과 같이 보입니다.

for(Type name:collection)

여기서 collection는 컬렉션 변수의 이름이고, Type는 컬렉션에 있는 요소의 유형이며, name루프가 반복될 때마다 컬렉션에서 다음 값을 가져오는 변수의 이름입니다.

이러한 종류의 루프는 암시적 반복자를 사용하여 컬렉션의 모든 요소를 ​​반복합니다. 이것이 실제로 작동하는 방식입니다.

for-each 루프 컴파일러가 보는 것: 반복자를 사용한 루프
ArrayList<String> list = new ArrayList<String>();

for (String str: list)
{
   System.out.println(str);
}
ArrayList<String> list = new ArrayList<String>();
Iterator<String> it = list.iterator();

while (it.hasNext())
{
   String str = it.next();
   System.out.println(str);
}

컴파일러는 for-each코드에서 루프를 만나면 간단히 오른쪽에 있는 코드로 바꿉니다. 누락된 다른 메서드 호출과 함께 반복자를 가져오는 호출을 추가합니다.

프로그래머는 루프를 좋아 for-each하고 컬렉션의 모든 요소를 ​​반복해야 할 때 거의 항상 루프를 사용합니다.

ArrayList루프 를 사용하여 목록을 반복하는 것조차 for-each더 짧아 보입니다.

for-each 루프 for 루프
ArrayList<String> list = new ArrayList<String>();

for (String str: list)
{
   System.out.println(str);
}
ArrayList<String> list = new ArrayList<String>();

for (int i = 0; i < list.size(); i++)
{
   String str = list.get(i);
   System.out.println(str);
}


for-each4. 루프 에서 요소 제거

루프 for-each에는 한 가지 단점이 있습니다. 요소를 올바르게 제거할 수 없다는 것입니다. 이렇게 코드를 작성하면 오류가 발생합니다.

암호 메모
ArrayList<String> list = new ArrayList<String>();

list.add("Hallo");
list.add("Hello");
list.add("Hola");
list.add("Bonjour");
list.add("Ciao");
list.add("Namaste");

for (String str: list)
{
   if (str.equals("Hello"))
      list.remove(str);
}












제거 작업은 오류를 생성합니다!

이것은 매우 훌륭하고 이해하기 쉬운 코드이지만 작동하지 않습니다.

중요한!

반복자를 사용하여 순회하는 동안에는 컬렉션을 변경할 수 없습니다.

이 제한을 우회하는 세 가지 방법이 있습니다.

1. 다른 종류의 루프 사용

When traversing an ArrayList collection, 카운터 변수와 함께 일반 루프를 사용할 수 있습니다 i.

암호
for (int i = 0; i < list.size(); i++)
{
   String str = list.get(i);

   if (str.equals("Hello"))
   {
      list.remove(str);
      i--; // We need to decrease i, because the remove operation shifted the elements
   }
}

그러나 이 옵션은 컬렉션에 적합하지 HashSet않습니다 HashMap.

2. 명시적 이터레이터 사용

반복자를 명시적으로 사용하고 해당 remove()메서드를 호출할 수 있습니다.

작동하는 버전 작동하지 않는 버전
Iterator<String> it = set.iterator();
while (it.hasNext())
{
   String str = it.next();
   if (str.equals("Hello"))
       it.remove();
}
for (String str: list)
{
   if (str.equals("Hello"))
      list.remove(str);
}

remove()반복자 개체에서 메서드를 호출한다는 점에 유의하세요 ! 반복자는 항목이 제거되었음을 인식하고 상황을 올바르게 처리할 수 있습니다.

3. 컬렉션 사본 사용

컬렉션의 복사본을 만든 다음 루프에서 복사본을 사용하고 for-each원본 컬렉션에서 요소를 삭제할 수도 있습니다.

암호 메모
ArrayList<String> listCopy = new ArrayList(list);

for (String str: listCopy)
{
   if (str.equals("Hello"))
      list.remove(str);
}
컬렉션의 복사본을 만드는 것은 매우 쉽습니다.



루프는 컬렉션의 복사본에 반복자를 사용합니다.
컬렉션 에서 요소가 제거됩니다 list.

컬렉션은 요소 자체가 복제되지 않기 때문에 오히려 빠르게 복사됩니다. 대신 새 컬렉션은 이전 컬렉션에 이미 있는 요소에 대한 참조를 저장합니다.