Java 언어에 다소 익숙하지 않기 때문에 목록 (또는 다른 컬렉션)과 각각의 장단점을 반복 할 수있는 모든 방법 (또는 적어도 비 병리학 적 방법)에 익숙해 지려고합니다.
List<E> list
객체가 주어지면 모든 요소를 반복하는 다음과 같은 방법을 알고 있습니다.
기본 for 루프 (물론 동등한 while
/ do while
루프도 있습니다)
// Not recommended (see below)!
for (int i = 0; i < list.size(); i++) {
E element = list.get(i);
// 1 - can call methods of element
// 2 - can use 'i' to make index-based calls to methods of list
// ...
}
참고 : @amarseillan가 지적한 바와 같이,이 양식을 통해 반복에 대한 빈약 한 선택 List
의 실제 구현하기 때문에의이 get
방법은 사용시 효율적으로하지 않을 수 있습니다 Iterator
. 예를 들어, LinkedList
구현에서는 i 번째 요소를 가져 오려면 i 앞에있는 모든 요소를 통과해야합니다.
위의 예제에서 List
구현이 미래의 반복을보다 효율적으로 만들기 위해 “위치를 저장” 할 방법이 없습니다 . 들어 ArrayList
복잡성 / 비용이 있기 때문에, 실제로 문제가되지 않는 get
시간 상수는 (O (1))을가 반면, LinkedList
그리스트의 크기에 비례한다 (O (N)).
내장 Collections
구현 의 계산 복잡성에 대한 자세한 내용은 이 질문을 확인하십시오 .
향상된 for 루프 ( 이 질문에 잘 설명 되어 있음 )
for (E element : list) {
// 1 - can call methods of element
// ...
}
반복자
for (Iterator<E> iter = list.iterator(); iter.hasNext(); ) {
E element = iter.next();
// 1 - can call methods of element
// 2 - can use iter.remove() to remove the current element from the list
// ...
}
ListIterator
for (ListIterator<E> iter = list.listIterator(); iter.hasNext(); ) {
E element = iter.next();
// 1 - can call methods of element
// 2 - can use iter.remove() to remove the current element from the list
// 3 - can use iter.add(...) to insert a new element into the list
// between element and iter->next()
// 4 - can use iter.set(...) to replace the current element
// ...
}
기능성 Java
list.stream().map(e -> e + 1); // Can apply a transformation function for e
Iterable.forEach , Stream.forEach , …
(Java 8 ‘s Stream API의 맵 메소드 (@i_am_zero의 답변 참조)
구현하는 Java 8 콜렉션 클래스 Iterable
(예 : all List
)에는 이제 위에서 설명한 for 루프 명령문forEach
대신 사용할 수 있는 메소드가 있습니다 . (다음은 좋은 비교를 제공하는 또 다른 질문 입니다.)
Arrays.asList(1,2,3,4).forEach(System.out::println);
// 1 - can call methods of an element
// 2 - would need reference to containing object to remove an item
// (TODO: someone please confirm / deny this)
// 3 - functionally separates iteration from the action
// being performed with each item.
Arrays.asList(1,2,3,4).stream().forEach(System.out::println);
// Same capabilities as above plus potentially greater
// utilization of parallelism
// (caution: consequently, order of execution is not guaranteed,
// see [Stream.forEachOrdered][stream-foreach-ordered] for more
// information about this).
다른 방법이 있다면 무엇입니까?
(BTW, 저의 관심은 전혀 성능 을 최적화 하려는 욕구에서 비롯되지 않습니다 . 개발자로서 어떤 형식을 사용할 수 있는지 알고 싶습니다.)
답변
루핑의 세 가지 형태는 거의 동일합니다. 향상된 for
루프 :
for (E element : list) {
. . .
}
에 따르면, Java 언어 사양 , 동일한 전통과 반복자의 명시 적 사용에 효과 for
루프. 세 번째 경우에는 현재 요소를 제거하여 목록 내용 만 수정 한 다음 remove
반복자 자체 의 방법을 통해 목록 요소를 수정하는 경우에만 목록 내용을 수정할 수 있습니다. 인덱스 기반 반복을 사용하면 어떤 방식 으로든 목록을 자유롭게 수정할 수 있습니다. 그러나 현재 색인 앞에 오는 요소를 추가하거나 제거하면 루프에서 요소를 건너 뛰거나 동일한 요소를 여러 번 처리 할 위험이 있습니다. 그러한 변경을 할 때 루프 인덱스를 올바르게 조정해야합니다.
모든 경우에 element
실제 목록 요소에 대한 참조입니다. 반복 방법 중 어느 것도 목록의 내용을 복사하지 않습니다. 내부 상태에 대한 변경 사항 element
은 항상 목록에서 해당 요소의 내부 상태에 표시됩니다.
기본적으로 목록을 반복하는 두 가지 방법은 인덱스를 사용하거나 반복자를 사용하는 것입니다. 향상된 for 루프는 반복자를 명시 적으로 정의하는 것을 피하기 위해 Java 5에 도입 된 구문 바로 가기입니다. 두 스타일 모두 for
, while
또는 do while
블록을 사용하여 본질적으로 사소한 변형을 만들 수 있지만 모두 동일한 것 (또는 오히려 두 가지)으로 요약됩니다.
편집 : @ iX3이 주석에서 지적한 것처럼 a를 사용하여 ListIterator
반복 할 때 목록의 현재 요소를 설정할 수 있습니다 . 루프 변수를 초기화하는 List#listIterator()
대신 사용해야 List#iterator()
합니다 (확실히 a ListIterator
대신 선언해야 합니다 Iterator
).
답변
질문에 나열된 각 종류의 예 :
ListIterationExample.java
import java.util.*;
public class ListIterationExample {
public static void main(String []args){
List<Integer> numbers = new ArrayList<Integer>();
// populates list with initial values
for (Integer i : Arrays.asList(0,1,2,3,4,5,6,7))
numbers.add(i);
printList(numbers); // 0,1,2,3,4,5,6,7
// replaces each element with twice its value
for (int index=0; index < numbers.size(); index++) {
numbers.set(index, numbers.get(index)*2);
}
printList(numbers); // 0,2,4,6,8,10,12,14
// does nothing because list is not being changed
for (Integer number : numbers) {
number++; // number = new Integer(number+1);
}
printList(numbers); // 0,2,4,6,8,10,12,14
// same as above -- just different syntax
for (Iterator<Integer> iter = numbers.iterator(); iter.hasNext(); ) {
Integer number = iter.next();
number++;
}
printList(numbers); // 0,2,4,6,8,10,12,14
// ListIterator<?> provides an "add" method to insert elements
// between the current element and the cursor
for (ListIterator<Integer> iter = numbers.listIterator(); iter.hasNext(); ) {
Integer number = iter.next();
iter.add(number+1); // insert a number right before this
}
printList(numbers); // 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15
// Iterator<?> provides a "remove" method to delete elements
// between the current element and the cursor
for (Iterator<Integer> iter = numbers.iterator(); iter.hasNext(); ) {
Integer number = iter.next();
if (number % 2 == 0) // if number is even
iter.remove(); // remove it from the collection
}
printList(numbers); // 1,3,5,7,9,11,13,15
// ListIterator<?> provides a "set" method to replace elements
for (ListIterator<Integer> iter = numbers.listIterator(); iter.hasNext(); ) {
Integer number = iter.next();
iter.set(number/2); // divide each element by 2
}
printList(numbers); // 0,1,2,3,4,5,6,7
}
public static void printList(List<Integer> numbers) {
StringBuilder sb = new StringBuilder();
for (Integer number : numbers) {
sb.append(number);
sb.append(",");
}
sb.deleteCharAt(sb.length()-1); // remove trailing comma
System.out.println(sb.toString());
}
}
답변
목록의 구현을 모르므로 기본 루프는 권장되지 않습니다.
그것이 LinkedList 인 경우, 각 호출은
list.get(i)
목록을 반복하여 N ^ 2 시간 복잡성을 초래합니다.
답변
JDK8 스타일 반복 :
public class IterationDemo {
public static void main(String[] args) {
List<Integer> list = Arrays.asList(1, 2, 3);
list.stream().forEach(elem -> System.out.println("element " + elem));
}
}
답변
에서 자바 (8) 우리는 컬렉션 클래스 반복하는 여러 가지 방법이있다.
Iterable forEach 사용하기
구현하는 컬렉션 Iterable
(예 : 모든 목록)에 forEach
메서드가 있습니다. Java 8에 도입 된 메소드 참조를 사용할 수 있습니다 .
Arrays.asList(1,2,3,4).forEach(System.out::println);
forEach 및 forEachOrdered에 스트림 사용
Stream 을 다음과 같이 사용하여 목록을 반복 할 수도 있습니다 .
Arrays.asList(1,2,3,4).stream().forEach(System.out::println);
Arrays.asList(1,2,3,4).stream().forEachOrdered(System.out::println);
우리는 좋아한다 forEachOrdered
동안 forEach
의 행동이 있기 때문에 forEach
명시 적으로 비 결정적인 곳으로 forEachOrdered
수행 스트림이 정의 된 만남 순서가있는 경우 스트림의 만남 순서에서이 스트림의 각 요소에 대한 작업. 따라서 forEach는 주문 유지를 보장하지 않습니다.
스트림의 장점은 필요한 경우 병렬 스트림을 사용할 수도 있다는 것입니다. 주문과 상관없이 항목을 인쇄하는 것이 목적이라면 병렬 스트림을 다음과 같이 사용할 수 있습니다.
Arrays.asList(1,2,3,4).parallelStream().forEach(System.out::println);
답변
나는 당신이 병리학 적이라고 생각하는 것을 모르지만 이전에는 볼 수 없었던 대안을 제공하겠습니다.
List<E> sl= list ;
while( ! sl.empty() ) {
E element= sl.get(0) ;
.....
sl= sl.subList(1,sl.size());
}
또는 재귀 버전 :
void visit(List<E> list) {
if( list.isEmpty() ) return;
E element= list.get(0) ;
....
visit(list.subList(1,list.size()));
}
또한 고전의 재귀 버전 for(int i=0...
:
void visit(List<E> list,int pos) {
if( pos >= list.size() ) return;
E element= list.get(pos) ;
....
visit(list,pos+1);
}
나는 당신이 “약간 Java에 익숙하지 않기”때문에 이것들을 언급하는데 이것은 흥미로울 수있다.
답변
Java 8부터 forEach 를 사용할 수 있습니다 .
List<String> nameList = new ArrayList<>(
Arrays.asList("USA", "USSR", "UK"));
nameList.forEach((v) -> System.out.println(v));