일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- SQL
- html
- Stock
- 맥
- 주식 청약
- Stock ipo
- 제이쿼리
- linux
- java
- 오라클
- Eclipse
- MYSQL
- 자바스크립트
- css
- 주식 청약 일정
- 공모주 청약
- codeigniter
- 리눅스
- 코드이그나이터
- 공모주
- 7월 공모주 청약 일정
- 공모주 청약 일정
- IPO
- jquery
- Oracle
- 6월 공모주 청약 일정
- 주식
- php
- JavaScript
- 자바
- Today
- Total
개발자의 끄적끄적
[java] 리스트돌릴땐 무조건 foreach를 사용하자 [펌] 본문
[java] 리스트돌릴땐 무조건 foreach를 사용하자 [펌]
Java5 에서부터 for-each 문이 추가됐다. 특별히 새로운 문법이 추가된게 아니라 기존 for문을 활용하는거라 for-each라고하면 못알아듣는 사람도 있고, 향상된 for문이라고 말하는사람도 있고.. 특히 요즘엔 stream API에 forEach() 메서드까지 추가되면서 의사소통에 약간 혼란스럼이 있긴하지만 보면 다들 알것이다.
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
for(Integer i : list){
System.out.println(i);
}
for-each 문은 인덱스를 명시할 필요없이 알아서 리스트 사이즈만큼 반복되기때문에 에러여지도 없어지고 코드도 간결해지는 매우 유용한 문법이지만 한가지 치명적인 문제점이 있다.
그것은 인덱스를 사용할 수 없다는 것이다.
얼마전 현업에서 이 문제에 부딪힌적이 있었는데 아무생각없이 인덱스를 사용하기 위한 반복문을 돌렸다.
static <T> void method01(List<T> list){
for(int i = 0; i < list.size(); i++){
T t = list.get(i);
// 인덱스를 사용하는 추가코드...
}
}
이 코드는 코드리뷰에서 탈탈 털렸는데 리스트를 일반 for문으로 돌렸기때문이다.
for-each에 비해 인덱스를 계산하는 코드가 좀 더 추가되는것 외에 어떤 문제가 있기에 일반 for문이 털린걸까?
작은 SI를 다니던시절부터 다른건 몰라도 컬렉션 구현체들만큼은 인터페이스를 이용해 참조했다,
습관처럼.
List<String> list1 = new ArrayList<>();
List<String> list2 = new LinkedList<>();
Set<String> set1 = new HashSet<>();
Set<String> set2 = new LinkedHashSet<>();
Map<String, Object> map1 = new HashMap<>();
Map<String, Object> map2 = new LinkedHashMap<>();
왜 얘네만큼은 인터페이스로 선언하는지 궁금한 맘에 물어봤었지만 당시엔 답을 얻지 못했었다.
뭐 이번주제는 이게아니니까 간략히 말하면 인터페이스를 사용해서 다른 구현체를 사용할때도 유연하게 교체하기위함인데 문제는 이것 때문이다.
다시한번 위 소스를 보면 메서드의 인자는 List 타입으로 받고있다. List의 구현체는 대표적으로 ArrayList, LinkedList가 있을텐데 이 구현체에 따라서 get() 메서드의 시간복잡도가 극명하게 달라질 수 있다는 것이다.
LinkedList의 경우 인자로 전달된 인덱스의 요소를 가져오기위해서는 항상 첫 노드에서부터 찾아 들어가야하기때문에 get()의 시간복잡도는 O(n)이다.
for문과 결합되면 2중 반복문으로 돌아서 O(n^2)이 되는것이다.
static <T> void method01(List<T> list){
for(int i = 0; i < list.size(); i++){
T t = list.get(i);
// 인덱스를 사용하는 추가코드...
}
}
LinkedList는 get()메서드 내부에 이미 반복문이 있기때문에 의도치않게 2중반복이 되는것이다.
이걸 이해하는건 어렵지않았는데 여기서 한가지 개념없는 말대꾸를 했다.
"List로 받고는 있지만 해당 메서드에 전달되는 구현체는 ArrayList이고, 그럼 O(n^2)이 아니라 O(n)인거 아니에요?"
"ArrayList를 확정짓고 코딩할거면 왜 List로 받아요?"
여기서 들려온 대답은 내 머리를 쿵 치는것 같았다.
왜 List로 선언하는지 머리로는 다 알고있었지만 이유를 알고있으면서도 습관처럼 List로 선언하고있었던거였다.
구현체는 언제든 바뀔 수 있고,
설사 내가 LinkedList를 쓰지않더라도 List를 반환하는 외부 라이브러리에서 LinkedList 인스턴스를 반환할 수도 있는 법이다.
다형성을 이용해 구현체에 상관없게끔 코드를 짜고있었다면 머리속에서도 구현체를 지워야하는 것이었다.
자, 그럼 List를 반복할땐 무조건 for-each문을 사용해야하는건 알았다. 그런데 그럼 인덱스는 어떻게해야하나?
static <T> void method01(List<T> list){
int i = 0;
for(T t : list){
// 인덱스를 사용하는 추가코드...
i++;
}
}
뭐 특별한 해결법은 없다. 다만 개인적으로 인덱스가 필요한 경우는 메서드 분리나 구조변경을 통해 리팩토링이 가능한 경우가 많았다.
List를 반복할때는 꼭 for-each나 Iterator를 사용하고, 인덱스가 필요한 경우는 명시적으로 인덱스를 사용하지않을 수 있게 리팩토링할 수는 없는지 고민해보자.
출처: https://multifrontgarden.tistory.com/130 [우리집앞마당]
'개발 > java & jsp' 카테고리의 다른 글
[자바] SFTP 파일업로드 디렉토리 생성 (0) | 2020.03.01 |
---|---|
[java] SFTP 사용방법 (jsch 라이브러리 사용) (0) | 2020.02.29 |
[java] 자바(java)로 만든 FTP와 SFTP Client 통합 프로그램 [펌] (0) | 2020.02.28 |
[java] [Eclipse]java프로젝트에 jar파일 추가하기, jar파일 상대경로로 넣기 [펌] (0) | 2020.02.27 |
[java] eclipse Could not create the Java virtual machine 에러 해결방법 (0) | 2020.02.25 |