-
item58. 전통적인 for문보다는 for-each문을 사용하라.✅ 핵심 정리
⚓️서론이 아이템장에서는 전통적인 for문을 사용해보고 문제점을 파악하고, 그 문제점을 해결하는 for-each문을 배울 것이다. 1️⃣전통적인 for 문으로 컬렉션을 순회하는 코드.➡️컬렉션 순회하기 - for loopfor(iterator<Element> i = c.iterator(); i.hasNext(); ){
Element e = i.next();
//do something
} ➡️배열 순회하기String[] str = new String[]{"a","b","c"};
for(int i = 0; i < str.length; i++){
String element = arr[i];
//do something
} ➡️컬렉션 순회하기 - whileIterator<Element> i = c.iterator();
while(iterator.hasNext()){
Element e = i.next();
//do something
} for loop와 배열 순회하기는 반복자와 인덱스 변수 모두 코드를 지저분하게 한다. 또한 이렇게 쓴다면 요소 종류가 늘어나면 오류가 생길 가능성이 높아진다. 혹시라도 잘못된 변수를 사용했을 때 컴파일러가 잡아준다는 보장도 없다. 컬렉션이냐 배열이냐에 따라 코드 형태가 상당히 달라지므로 주의해야한다. 우리에게 필요한 것은 원소들뿐이다. 해결책은 2️⃣for-each를 알아보자.for-each를 사용하면
➡️컬렉션과 배열을 순회하는 올바른 관용구for(Element e : elements){
... // do something
}
컬렉션 중첩 사용🔡카드 예)public class Card {
private final Suit suit;
private final Rank rank;
enum Suit { CLUB, DIAMOND, HEART, SPADE }
enum Rank { ACE, DEUCE, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT,
NINE, TEN, JACK, QUEEN, KING }
static Collection<Suit> suits = Arrays.asList(Suit.values());
static Collection<Rank> ranks = Arrays.asList(Rank.values());
Card(Suit suit, Rank rank ) {
this.suit = suit;
this.rank = rank;
}
public static void main(String[] args) {
List<Card> deck = new ArrayList<>();
for (Iterator<Suit> i = suits.iterator(); i.hasNext(); )
for (Iterator<Rank> j = ranks.iterator(); j.hasNext(); )
deck.add(new Card(i.next(), j.next()));
}
}
➡️왜 NoSuchElementException을 던질까?바깥 컬렉션(suits)의 반복자에서 next메서드가 너무 많이 불린다. → 마지막 줄의 i.next()를 주목 next()는 숫자(suit) 하나당 한 번씩만 불러야하는데, 안쪽 반복문에서 호출되기 때문에 카드(Rank) 하나당 한번 씩 불리고 있다. 따라서 숫자가 바닥나면 반복문에서 ➡️위 코드를 for-each로 만들면for (Suit suit : suits) {
for (Rank rank : ranks) {
deck.add(new Card(suit, rank));
}
} 코드가 정말 간결해지고 가독성이 좋아진다. 머리가 덜 아프다.
ACE-----CLUB
DEUCE-----CLUB
THREE-----CLUB
FOUR-----CLUB
FIVE-----CLUB
SIX-----CLUB
SEVEN-----CLUB
EIGHT-----CLUB
NINE-----CLUB
TEN-----CLUB
JACK-----CLUB
QUEEN-----CLUB
KING-----CLUB
ACE-----DIAMOND
DEUCE-----DIAMOND
THREE-----DIAMOND
FOUR-----DIAMOND
FIVE-----DIAMOND
SIX-----DIAMOND
SEVEN-----DIAMOND
EIGHT-----DIAMOND
NINE-----DIAMOND
TEN-----DIAMOND
JACK-----DIAMOND
QUEEN-----DIAMOND
KING-----DIAMOND
ACE-----HEART
DEUCE-----HEART
THREE-----HEART
FOUR-----HEART
FIVE-----HEART
SIX-----HEART
SEVEN-----HEART
EIGHT-----HEART
NINE-----HEART
TEN-----HEART
JACK-----HEART
QUEEN-----HEART
KING-----HEART
ACE-----SPADE
DEUCE-----SPADE
THREE-----SPADE
FOUR-----SPADE
FIVE-----SPADE
SIX-----SPADE
SEVEN-----SPADE
EIGHT-----SPADE
NINE-----SPADE
TEN-----SPADE
JACK-----SPADE
QUEEN-----SPADE
KING-----SPADE 🔡주사위 예시)
public class DiceRolls {
enum Face { ONE, TWO, THREE, FOUR, FIVE, SIX }
public static void main(String[] args) {
// 같은 버그, 다른 증상!
Collection<Face> faces = EnumSet.allOf(Face.class);
for (Iterator<Face> i = faces.iterator(); i.hasNext(); )
for (Iterator<Face> j = faces.iterator(); j.hasNext(); )
System.out.println(i.next() + " " + j.next());
}
}
ONE ONE
TWO TWO
THREE THREE
FOUR FOUR
FIVE FIVE
SIX SIX
*************************** 위 코드는 예외는 반환하지 않는다. 하지만 가능한 조합을 단 여섯 쌍만 출력하고 끝나버린다. 원래는 36개 조합이 나와야 한다. 이 문제를 해결하려면 바깥 반복문에 바깥 원소를 저장하는 변수를 하나 추가해야 한다. 🔡문제 해결 코드public class DiceRolls {
enum Face { ONE, TWO, THREE, FOUR, FIVE, SIX }
public static void main(String[] args) {
Collection<Face> faces = EnumSet.allOf(Face.class);
for (Face f1 : faces){
for (Face f2 : faces){
System.out.println(f1 + " " + f2);
}
}
}
}
ONE ONE
ONE TWO
ONE THREE
ONE FOUR
ONE FIVE
ONE SIX
TWO ONE
TWO TWO
TWO THREE
TWO FOUR
TWO FIVE
TWO SIX
THREE ONE
THREE TWO
THREE THREE
THREE FOUR
THREE FIVE
THREE SIX
FOUR ONE
FOUR TWO
FOUR THREE
FOUR FOUR
FOUR FIVE
FOUR SIX
FIVE ONE
FIVE TWO
FIVE THREE
FIVE FOUR
FIVE FIVE
FIVE SIX
SIX ONE
SIX TWO
SIX THREE
SIX FOUR
SIX FIVE
SIX SIX for-each는 코드를 완전히 깔끔히 만들면서 간단히 해결했다. 하지만 for-each문을 사용할 수 없는 상황이 있다. ➡️for-each문을 사용할 수 없는 상황1️⃣ 파괴적인 필터링(destructive filtering)
2️⃣ 변형(transforming)
3️⃣병렬 반복(parallel iteration)
세 가지 상황 중 하나에 속할 때는 일반적으로 for문을 사용한다. |
Beta Was this translation helpful? Give feedback.
Replies: 1 comment
-
자바의 Collection 타입 경우 구현체의 ArrayList와 같이 특정 인덱스에 접근할 수 있는 구현체도 있지만 그렇지 않은 경우도 있습니다. for (Iterator<Suit> i = suits.iterator(); i.hasNext(); )
for (Iterator<Rank> j = ranks.iterator(); j.hasNext(); )
deck.add(new Card(i.next(), j.next()));
다만 Iterator의 haNext와 next 이용해서도 모든 카드의 조합을 위 같은 에러가 발생하지 않고 작성할 수 있습니다.for (Iterator<Suit> i = suits.iterator(); i.hasNext();) {
Suit nextSuit = i.next();
for (Iterator<Rank> j = ranks.iterator(); j.hasNext();) {
deck.add(new Card(nextSuit, j.next()));
}
} 하지만 IntelliJ가 foreach를 추천하고 있습니다.
|
Beta Was this translation helpful? Give feedback.
자바의 Collection 타입 경우 구현체의 ArrayList와 같이 특정 인덱스에 접근할 수 있는 구현체도 있지만 그렇지 않은 경우도 있습니다.
이러한 경우 Iterator를 이용하거나 foreach를 활용하여 내부 값들을 조회해야 합니다.
다만 Iterator의 haNext와 next 이용해서도 모든 카드의 조합을 위 같은 에러가 발생하지 않고 작성할 수 있습니다.
하…