'10.03.19 story JAVA이야기2010. 4. 1. 13:34
<Effective Java> 2:00 ~ 6:00
for-each 루프는 종전의 for루프에 비해 엄청난 장점을 제공해준다. 서능 저하가 없으면서 명료하고 버그를 방지해준다. 따라서 사용 가능한 곳이라면 어디에서든 for-each루프를 사용해야 한다. 그러나 불행하게도 for-each루프를 사용할 수 없는 3가지 경우가 있다.
필터링 - 만일 어떤 컬렉션의 요소들을 오가면서 선택된 요소들을 삭제할 필요가 있다면, 명시적인 순환자를 사용할 필요가 있다. 그 순환자의 remove메소드를 호출해야 하기 때문이다.
변환 - 만일 List나 배열의 요소들을 오가면서 그 요소들의 일부 또는 모든 값을 변경할 필요가 있다면, 요소의 값을 지정하기 위해 List의 순환자나 배열의 인덱스가 필요하다.
병행 반복처리 - 만일 병행으로 여러 컬렉션의 요소들을 오가면서 처리할 필요가 있다면, 순환자나 인덱스 변수를 명시적으로 제어할 필요가 있다. 모든 순환자나 인덱스 변수들이 병행으로 처리될 수 있게 하기 위함이다.
라이브러리를 배우고 사용하자
표준 라이브러리를 사용하면, 그것을 작성한 전문가들의 지식과 더 앞서 사용한 사람들의 경험을 이용한다는 것이다.
프로그래머라면 java.lang, java.util, java.io 패키지의 내용들에 익숙해져야 한다. 다른 라이브러리는 필요할 때 파악하면 된다.
자바 1.5배포판에서 동시성 유틸리티들이 java.util.concurrent 패키지에 추가되었다. 이 패키지는 고수준의 동시성 유틸리티(다중 스레드 프로그래밍을 단순화하는)와 저수준의 동시성 원천 타입 모두를 표현한다. java.util.concurrent의 고수준 동시성 관련 부분도 모든 프로그래머의 기본 도구임이 틀림없다.
정확한 계산에는 double이나 float타입을 쓰지 말자
float와 double타입은 돈 계산에는 부적합하다. 이 타입은 정확한 결과를 제공하지 않는다.
package API;
import java.util.Random;
public class Main {
private static final Random rnd = new Random();
public static void main(String[] args) {
double funds = 1.00;
int itemsBought = 0;
for(double price = .10; funds >= price; price+=.10){
funds -=price;
itemsBought++;
}
System.out.println(itemsBought + "items bought");
System.out.println("Change : $"+funds);
}
static int random(int n){
return Math.abs(rnd.nextInt(n));
}
}
이 문제와 같이 돈 계산을 할 때 올바른 답을 구하려면 BigDemical, int, long 타입 중 하나를 사용해야 한다.
취급하는 수의 크기가 십진수 9자리를 넘지 않으면, int타입을 사용할 수 있고, 18자리를 넘지 않으면 long타입을 사용할 수 있으며, 18자리를 초과하면 BigDecimal을 사용해야 한다.
박스화 기본형보다는 기본형을 사용하자.
자바는 두 종류의 타입 시스템을 갖고 있다. int, double, boolean과 같은 기본형(primitives)과 String이나 List같은 참조타입이다. 그리고 모든 기본형은 자신과 대응되는 박스화 기본형(boxed primitive)을 갖고 있다. 예를 들어, int에 대응되는 박스화 기본형은 Integer이고, double은 Double, boolean은 Boolean, long Long...이 있다.
오토박싱과 오토언박싱이 언어에 추가되었다.
기본형과 박스화 기본형 간에는 크게 3가지 차이점이 있다.
첫 번째로, 기본형은 자신의 값만을 갖는 반면, 박스화 기본형은 자신의 값과는 별개로 식별성을 갖는다. 달리 말해, 두 개의 박스화 기본형 인스턴스가 갖는 값은 같지만 식별성은 다를 수 있다는 것이다. 두 번째로, 기본형은 완전한 기능 값만을 갖는 반면, 각 박스화 기본형은 자신과 대응되는 기본형이 가질 수 있는 모든 기능 값에 추가하여 비 기능 값인 null을 갖는다. 끝으로, 기본형은 일반적으로 박스화 기본형에 비해 실행 시간과 메모리 사용 효율이 좋다. 따라서 우리가 조심하지 않으면 이런 세가지 차이점으로 인해 실제 문제가 생길 수 있다.
오토 박싱 - 기본형 값을 박스화 기본형 값으로 변환
오토언박싱 - 박스화 기본형 값을 기본형 값으로 변환
Comparator<Integer> natualOrder = new Comparator<Integer>(){
public int compare(Integer first, Integer second){
return first<second? -1 : (first == second ? 0 :1);
}
};
System.out.println(natualOrder.compare(3, 4));
System.out.println(natualOrder.compare(-2,-3));
//first == second을 실행하게 되는데 이 때는 인스턴스 값이 아닌 객체 참조를 비교하게 된다. 둘은 서로 다른 객체이므로 1이 출력된다.
System.out.println(natualOrder.compare(new Integer(43), new Integer(43)));
== 비교 연산자를 박스화 기본형에 적용하면 대부분 틀린 결과가 나온다.
이 문제를 해결하는 방법은 두 개의 지역변수를 추가하여 first와 second값을 지역 변수에 저장한 후 이 변수들로 비교하는 것이다.
package API;
public class Unbelievable {
static Integer i
public static void main(String[] args){
//Integer를 int와 비교하게 된다. 거의 모든 경우에서 기본형과 박스화 기본형을 하나의 연산에 섞어 놓으면
//박스화 기본형이 오토언박싱된다. 이 경우도 예외가 아니다. 만일 null객체 참조가 언오토박싱되면
//NullPointerException예외가 발생한다.
if(i==43)
System.out.println("Unbelievable");
}
}
박스화 기본형을 언제 사용해야 할까? 합당하게 사용할 수 있는 경우가 몇가지 있다. 첫 번째는 컬렉션의 요소, 키 값으로 사용하는 것이다. 컬렉션의 요소로는 기본형 값을 수 없다. 따라서, 이때는 박스화 기본형을 사용해야 한다. 두 번째는 특별한 경우라서, 매개 변수화 타입의 타입매개변수로 박스화 기본형을 사용해야 한다. 자바 언어에서 기본형의 사용을 허용하지 않기 때문이다. 예를 들어, ThreadLocal<Integer>와 같이 변수 타입을 선언할 수 없다. 따라서 그 대신 ThreadLocal<Integer>를 사용해야 한다. 끝으로, 리플렉션을 이용하여 재귀적인 매소드 호출을 할 때는 박스화 기본형을 사용해야 한다.
요약하면 박스화 기본형보다는 기본형을 사용하자. 기본형이 더 간단하고 실행도 더 빠르다. 만일 반드시 박스화 기본형을 사용해야 한다면 조심하자! 오토박싱은 박스화 기본형을 사용할 때 코드를 줄여준다. 박스화기본형의 인스턴스를 우리가 생성하고 초기화하지 않으면, 언박싱 할 때 NullPointerException 예외가 발생할 수 있다. 끝으로,기본형 값이 오토박싱 될 때는 불필요한 객체가 생성되고 비용도 많이 발생한다.
다른 타입을 쓸 수 있는 곳에서는 String사용을 피하자
String은 텍스트를 나타내기 위해 설계되어있으며, 자신의 역할을 잘 하고 있다. String은 공통적으로 많이 사용하고 있고 언어에서도 잘 지원한다.
이 항목에서는 String으로 해서는 안되는 것을 몇 가지 거론한다.
String으로 다른 값 타입을 대체하는 것은 좋지 않다.
즉 숫자 데이터가 들어가야 하는데 문자열 데이터로 정의해버린다던가 날짜 데이터가 들어가야하는데 문자열로 지정해버린다던가
String으로 enum타입을 대체 하는 것은 좋지 않다. enum타입은 String보다 훨씬 더 좋은 열거형 상수를 만든다.
String으로 집합 타입을 대체하는 것은 좋지 않다.
어떤 개체가 여러 개의 컴포넌트를 갖는 경우, 그것을 하나의 String으로 표현하는 것은 좋지 않은 생각이다. 예를 들어,
String compoundKey = className+"#"+i.next();
이 보다 더 좋은 방법은 집합체를 나타내는 클래스를 작성하는 것이다.
String으로 역량을 대체하는 것은 좋지 않다.
문자열 결합의 성능 저하를 주의하자
문자열 결합 연산자를 n개의 문자열에 반복적으로 사용하면 n의 제곱에 비례하는 시간이 걸린다. String이 불변이기 때문에 그런 불행한 결과를 초래하는 것이다.
public static String statement(){
String result = ""
for(int i=0; i<10000; i++)
result += "asdad\n"
return result;
}
//문자열 결합의 부적합한 사용
원하는 성능을 얻으려면 String 대신 StringBuilder를 사용하자.
public static String statement(){
StringBuilder b = new StringBuilder(1000*100);
for(int i=0; i < 10000; i++){
b.append("adadads");
}
System.out.println(b.toString());
return b.toString();
}
성능 차이가 많이 난다. 즉, 성능을 고려해서 몇 개 정도면 몰라도 그 이상의 문자열을 결합할 때는 문자열 결합 연산자를 사용하지 말자. 그리고 그 대신 StringBuilder클래스와 그 클래스의 append메소드를 사용하자. 또 다른 방법으로, 문자 타입을 저장하는 배열을 사용하거나, 또는 문자열을 결합하지 말고 한번에 하나씩 처리하자.
객체 참조는 그 객체의 인터페이스 타입으로 하자
'JAVA이야기' 카테고리의 다른 글
'10.03.22 story (0) | 2010.04.01 |
---|---|
'10.03.21 story (0) | 2010.04.01 |
'10.03.17 story (0) | 2010.04.01 |
'10.03.16 story (0) | 2010.04.01 |
'10.03.15 story (0) | 2010.04.01 |