개발관련/자바2010. 6. 10. 11:35
아래 내용은 채소좋아라는 분이 쓰신 글입니다.

----------------------------------------------------------------------------------------------------

태초에(Java 1.0 ~ 1.1)는

Java 언어에서 제공하는 key, value 쌍을 지원하는 데이터 Container는

Hashtable 클래스가 제공되었었습니다.

그런데 이 Hashtable 은 느리기도 하고 지원하는 메소드도 기능이 많이 부족해

 

Java 1.2 부터는 HashMap 를 포함해 여러 key,value 쌍을 지원하는 클래스가 새로 생겼는데요.

이와 동시에 이런 key, value 쌍을 쓰는 클래스들의 공통된 인터페이스를 Map 인터페이스로 묶었습니다.

(활용성을 높이기 위해)

Hashtable 도 Java 1.2 부터 Map을 implements 하게 시작합니다.

 

그래서

key,value 쌍을 지원하는 대부분의 클래스들은 Map 을 구현하게 되어있는데

대표적인 Hashtable, HashMap 은 key 와 value 를 hash 알고리즘에 의해 구현해 놓은 것입니다.

TreeMap 같은 경우는 이름에서부터 알수 있듯이 tree 알고리즘에 의해 key, value를 저장하고 있습니다.

구현체마다 독특한 특성이 있는데 HashMap의 경우 hash알고리즘에 의해 get이 매우 빠르며

TreeMap 의 경우 tree 소팅을 한상태로 저장함으로 data 소팅시 유리합니다.

 

그럼 같은 알고리즘을 쓰는 Hashtable과 HashMap 은 무슨 차이가 있냐면

Hashtable 의 모든 Data 변경 매소드는 syncronized 로 선언되어있습니다.

즉 매소드 호출 전 쓰레드간 동기화 락을 통해 멀티 쓰레드 환경에서 data의 무결성을 보장해줍니다.

반대로 HashMap 은 그런 선언이 없기 때문에 멀티 쓰레드에서 여러 쓰레드가 동시에 객체의

data 를 조작하는 경우 data가 깨져버리고 심각한 오류가 발생할 수 있습니다.

다만 이 동기화 락이 매우 느린 동작이기때문에 Hashtable 보다 HashMap 이 훨씬 빠릅니다.

Map 객체가 단일 쓰레드에서만 쓰일때는 HashMap을 사용해야합니다.

 

프로그래밍상의 편의성 때문에 멀티쓰레드 환경에서도 Hashtable 을 쓰기 보다는

HashMap을 다시 감싸서

 

Map m = Collections.synchronizedMap(new HashMap(...));


과 같은 형태가 최근에는 더 선호됩니다.

자세한 사용 설명은 javadoc 문서를 참고하세요.

http://java.sun.com/j2se/1.5.0/docs/api/java/util/HashMap.html

 

Java 1.5 환경부터는 cuncurrent util 이 default 로 제공되게 되어

멀티 쓰레드 환경에서 위 방법 보다는 CuncurrentHashMap 클래스를 사용하는게 더 선호됩니다.

CuncurrentHashMap 은 synchronizedMap 으로 감싸진 HashMap 이나 Hashtable 보다 더 빠르면서도 쓰레드간 동기화를 보장해 주는데

이는 동기화시 hashtable을 전체에 대해 lock을 걸지 않고 조각을 쪼개어 부분부분 lock 걸어

쓰레드간 경쟁이 심할시에 이득을 보는 구현입니다.

http://java.sun.com/j2se/1.5.0/docs/api/java/util/concurrent/ConcurrentHashMap.html

 

참고하시고

Java 라는 언어는 계속 발전하고 있음으로

새로운 버전이 나올때 새로운 기능에 관심을 갖을 필요가 있습니다.

'개발관련 > 자바' 카테고리의 다른 글

java의 for-each 구문 확장  (0) 2010.06.10
[펌]템플릿 형태 JSP  (0) 2010.01.06
[펌][JAVA] static키워드 바로알기  (0) 2009.12.18
[펌]GlassFish와 Tomcat 비교  (0) 2009.06.12
Java 5.x 이상에서 유용한 기능(generic)  (0) 2009.02.27
Posted by 자개비
개발관련/자바2010. 6. 10. 10:45

출처 : http://leepoint.net/notes-java/flow/loops/foreach.html

for each loop는 java 5.0(1.5)에서 확장된 개념이다. 아래에 이전 버전과 호환성 코드를 참고보면 이해가 쉽다.

For-each Loop

Purpose

The basic for loop was extended in Java 5 to make iteration over arrays and other collections more convenient. This newer for statement is called the enhanced for or for-each (because it is called this in other programming languages). I've also heard it called the for-in loop.

Use it in preference to the standard for loop if applicable (see last section below) because it's much more readable.

Series of values. The for-each loop is used to access each successive value in a collection of values.

Arrays and Collections. It's commonly used to iterate over an array or a Collections class (eg, ArrayList).

Iterable<E>. It can also iterate over anything that implements the Iterable<E> interface (must define iterator() method). Many of the Collections classes (eg, ArrayList) implement Iterable<E>, which makes the for-each loop very useful. You can also implement Iterable<E> for your own data structures.

General Form

The for-each and equivalent for statements have these forms. The two basic equivalent forms are given, depending one whether it is an array or an Iterable that is being traversed. In both cases an extra variable is required, an index for the array and an iterator for the collection.

For-each loop Equivalent for loop
for (type var : arr) {
    body-of-loop
}
for (int i = 0; i < arr.length; i++) { 
    type var = arr[i];
    body-of-loop
}
for (type var : coll) {
    body-of-loop
}
for (Iterator<type> iter = coll.iterator(); iter.hasNext(); ) {
    type var = iter.next();
    body-of-loop
}

Example - Adding all elements of an array

Here is a loop written as both a for-each loop and a basic for loop.

double[] ar = {1.2, 3.0, 0.8};
int sum = 0;
for (double d : ar) {  // d gets successively each value in ar.
    sum += d;
}

And here is the same loop using the basic for. It requires an extra iteration variable.

double[] ar = {1.2, 3.0, 0.8};
int sum = 0;
for (int i = 0; i < ar.length; i++) {  // i indexes each element successively.
    sum += ar[i];
}

Where the for-each is appropriate

Altho the enhanced for loop can make code much clearer, it can't be used in some common situations.

  • Only access. Elements can not be assigned to, eg, not to increment each element in a collection.
  • Only single structure. It's not possible to traverse two structures at once, eg, to compare two arrays.
  • Only single element. Use only for single element access, eg, not to compare successive elements.
  • Only forward. It's possible to iterate only forward by single steps.
  • At least Java 5. Don't use it if you need compatibility with versions before Java 5.
Posted by 자개비
개발관련/자바2009. 12. 18. 16:13
[JAVA] static키워드 바로알기

2007/10/16 11:27
| Posted by 이동한

자바를 한번쯤 공부해본사람이라면 static키워드를 모르지는 않을 것입니다.

하지만, 바르게 알고 있는 사람들은 그리 많지 않습니다.


자바경력자를 면접볼 때 static키워드에 대해서 질문하곤 합니다.


면접관 : static키워드에 대해서 설명해보세요.
응시자 : static키워드를 쓰면, 객체를 생성하지 않고도 변수나 함수를 사용할 수 있습니다.


면접관 : 왜 static키워드를 쓰나요?
응시자 : 객체를 생성하지 않아도 되니까 편리하고 속도도 빠릅니다.


면접관 : 그렇다면 모든 변수와 함수에 static을 붙이는 것이 좋겠네요?
응시자 : 가능한한 static을 붙이는 것이 좋다고 생각합니다.


면접관 : 어떤 경우에 static을 붙일 수 있고, 어떤 경우에 static을 붙일 수 없습니까?
응시자 : ...


면접관 : 만일 당신이 새로운 클래스를 작성한다고 할 때, 어떤 경우에 static키워드를
             사용해야한다고 생각합니까?

응시자 : ...


대부분의 경우 위와 같은 내용으로 문답이 진행됩니다.

사실 응시자의 대답은 다 맞는 얘기입니다. 하지만, static의 핵심적인 개념을 모르기 때문에
어떤 경우에 왜 static을 사용해야하는지는 잘모르는 것 같습니다.


먼저 결론부터 간단히 정리하면 다음과 같습니다.


1.클래스를 설계할 때, 멤버변수 중 모든 인스턴스에 공통적으로 사용해야하는 것에 static을 붙인다.
 - 인스턴스를 생성하면, 각 인스턴스들은 서로 독립적기 때문에 서로 다른 값을 유지한다.
    경우에 따라서는 각 인스턴스들이 공통적으로 같은 값이 유지되어야 하는 경우 static을
    붙인다.


2. static이 붙은 멤버변수는 인스턴스를 생성하지 않아도 사용할 수 있다.
 - static이 붙은 멤버변수(클래스변수)는 클래스가 메모리에 올라갈때 이미 자동적으로
   생성되기 때문이다.


3. static이 붙은 메서드(함수)에서는 인스턴스 변수를 사용할 수 없다.
 - static이 메서드는 인스턴스 생성 없이 호출가능한 반면, 인스턴스 변수는 인스턴스를
    생성해야만 존재하기 때문에... static이 붙은 메서드(클래스메서드)를 호출할 때
    인스턴스가 생성되어있을수도 그렇지 않을 수도 있어서 static이 붙은 메서드에서
    인스턴스변수의 사용을 허용하지 않는다.
    (반대로, 인스턴스변수나 인스턴스메서드에서는 static이 붙은 멤버들을 사용하는 것이
     언제나 가능하다. 인스턴스변수가 존재한다는 것은 static이 붙은 변수가 이미 메모리에
     존재한다는 것을 의미하기 때문이다.)


4. 메서드 내에서 인스턴스 변수를 사용하지 않는다면, static을 붙이는 것을 고려한다.
 - 메서드의 작업내용중에서 인스턴스 변수를 필요로 한다면, static을 붙일 수 없다.
    반대로 인스턴스변수를 필요로 하지 않는다면, 가능하면 static을 붙이는 것이 좋다.
    메서드 호출시간이 짧아지기 때문에 효율이 높아진다.
    (static을 안붙인 메서드는 실행시 호출되어야할 메서드를 찾는 과정이 추가적으로
    필요하기 때문에 시간이 더 걸린다.)


5. 클래스 설계시 static의 사용지침
 - 먼저 클래스의 멤버변수중 모든 인스턴스에 공통된 값을 유지해야하는 것이 있는지
    살펴보고 있으면, static을 붙여준다.
 - 작성한 메서드 중에서 인스턴스 변수를 사용하지 않는 메서드에 대해서 static을
    붙일 것을 고려한다.
 
일반적으로 인스턴스변수와 관련된 작업을 하는 메서드는 인스턴스메서드(static이 안붙은
메서드)이고 static변수(클래스변수)와 관련된 작업을 하는 메서드는 클래스메서드(static이 붙은 메서드)라고 보면 된다.


다음은 static에 대한 자세한 설명과 예제입니다.


static은 객체지향개념을 이해하는 가장 중요한 첫걸음이니 확실히 알아두셔야합니다.


==================================================================
   


 

3.2 클래스변수와 인스턴스변수


클래스변수와 인스턴스변수의 차이를 이해하기 위한 예로 카드 게임에 사용되는 카드를 클래스로 정의해보자.



카드 클래스를 작성하기 위해서는 먼저 카드를 분석해서 속성과 기능을 알아 내야한다. 속성으로는 카드의 무늬, 숫자, 폭, 높이 정도를 생각할 수 있을 것이다.
이 중에서 어떤 속성을 클래스 변수로 선언할 것이며, 또 어떤 속성들을 인스턴스 변수로 선언할 것인지 생각해보자.


class Card {
     String kind ;                         // 카드의 무늬 - 인스턴스 변수
     int number;                         // 카드의 숫자 - 인스턴스 변수
     static int width = 100 ;             // 카드의 폭 - 클래스 변수
     static int height = 250 ;            // 카드의 높이 - 클래스 변수
}


각 Card인스턴스는 자신만의 무늬(kind)와 숫자(number)를 유지하고 있어야 하므로 이들을 인스턴스변수로 선언하였고, 각 카드들의 폭(width)과 높이(height)는 모든 인스턴스가 공통적으로 같은 값을 유지해야하므로 클래스변수로 선언하였다.

만일 카드의 폭을 변경해야할 필요가 있을 때는 모든 카드의 width값을 변경하지 않고, 한 카드의 width값만 변경해도 모든 카드의 width값이 변경되는 셈이다.

[예제6-4] CardTest.java

class CardTest{
      public static void main(String args[]) {
            // 클래스변수(static 변수)는 객체생성없이 '클래스이름.클래스변수'로 직접 사용 가능하다.
            System.out.println("Card.width = " + Card.width);
            System.out.println("Card.height = " + Card.height);

            Card c1 = new Card();
            c1.kind = "Heart";
            c1.number = 7;

            Card c2 = new Card();
            c2.kind = "Spade";
            c2.number = 4;

            System.out.println("c1은 " + c1.kind + ", " + c1.number + "이며, 크기는 (" + c1.width + ", " + c1.height + ")" );
            System.out.println("c2는 " + c2.kind + ", " + c2.number + "이며, 크기는 (" + c2.width + ", " + c2.height + ")" );             System.out.println("이제 c1의 width와 height를 각각 50, 80으로 변경합니다.");
            c1.width = 50;
            c1.height = 80;

            System.out.println("c1은 " + c1.kind + ", " + c1.number + "이며, 크기는 (" + c1.width + ", " + c1.height + ")" );
            System.out.println("c2는 " + c2.kind + ", " + c2.number + "이며, 크기는 (" + c2.width + ", " + c2.height + ")" );
      }
}

class Card {
     String kind ;                         // 카드의 무늬 - 인스턴스 변수
     int number;                         // 카드의 숫자 - 인스턴스 변수
     static int width = 100;             // 카드의 폭 - 클래스 변수
     static int height = 250;             // 카드의 높이 - 클래스 변수
}

[실행결과]
Card.width = 100
Card.height = 250
c1은 Heart, 7이며, 크기는 (100, 250)
c2는 Spade, 4이며, 크기는 (100, 250)
이제 c1의 width와 height를 각각 50, 80으로 변경합니다.
c1은 Heart, 7이며, 크기는 (50, 80)
c2는 Spade, 4이며, 크기는 (50, 80)


Card클래스의 클래스변수(static변수)인 width, height 그리고 color는 Card클래스의 인스턴스를 생성하지 않고도 '클래스이름.클래스변수'와 같은 방식으로 사용할 수 있다.
Card인스턴스인 c1과 c2는 클래스 변수인 width와 height를 공유하기 때문에, c1의 width와 height를 변경하면 c2의 width와 height값도 바뀐 것과 같은 결과를 얻는다.
Card.width, c1.width, c2.width는 모두 같은 저장공간을 참조하므로 항상 같은 값을 갖게 된다.
인스턴스 변수는 인스턴스가 생성될 때 마다 생성되므로 인스턴스마다 각기 다른 값을 유지할 수 있지만, 클래스 변수는 모든 인스턴스가 하나의 저장공간을 공유하므로, 항상 공통된 값을 갖는다.
[플래시동영상]자료실의 플래시동영상 MemberVar.swf을 꼭 보도록하자.



3.9 클래스메서드(static메서드)와 인스턴스메서드


변수에서 그랬던 것과 같이, 메서드 앞에 static이 붙어 있으면 클래스메서드이고 붙어 있지 않으면 인스턴스메서드이다.
클래스 메서드는 호출방법 역시 클래스변수처럼, 객체를 생성하지 않고도 '클래스이름.메서드이름(매개변수)'와 같은 식으로 호출이 가능하다.
그렇다면 어느 경우에 static을 사용해서 클래스메서드로 정의해야하는 것일까?

클래스는 '데이터(변수)와 데이터에 관련된 메서드의 집합'이라고 할 수 있다. 같은 클래스 내에 있는 메서드와 멤버변수는 아주 밀접한 관계가 있다. 인스턴스메서드는 인스턴스변수와 관련된 작업을 하는, 즉 메서드의 작업을 수행하는데 인스턴스변수를 필요로 하는 메서드이다.
그래서 인스턴스변수와 관계없거나(메서드 내에서 인스턴스변수를 사용하지 않거나), 클래스변수만을 사용하는 메서드들은 클래스메서드로 정의한다.

물론 인스턴스변수를 사용하지 않는다고 해서 반드시 클래스 메서드로 정의해야하는 것은 아니지만, 그렇게 하는 것이 일반적이다.
참고로 Math클래스의 모든 메서드는 클래스메서드임을 알 수 있다. Math클래스에는 인스턴스변수가 하나도 없거니와 Math클래스의 함수들은 작업을 수행하는데 필요한 값들을 모두 매개변수로 받아서 처리 하기 때문이다. 이처럼, 단순히 함수들만의 집합인 경우에는 클래스메서드로 선언한다.

[참고]인스턴스 변수 뿐만 아니라 인스턴스 메서드를 호출하는 경우에도 인스턴스 메서드로 선언되어야 한다. 인스턴스 메서드를 호출하는 것 역시 인스턴스 변수를 간접적으로 사용하는 것이기 때문이다.

[예제6-12] MyMathTest2.java

class MyMath2 {
      long a, b;
     
      // 인스턴스변수 a, b를 이용한 작업을 하므로 매개변수가 필요없다.
      long add() {       return a + b; }
      long subtract() {       return a - b; }
      long multiply() {       return a * b; }
      double divide() {       return a / b; }

      // 인스턴스변수와 관계없이 매개변수만으로 작업이 가능하다.
      static long add(long a, long b) {       return a + b; }
      static long subtract(long a, long b) {       return a - b; }
      static long multiply(long a, long b) {       return a * b; }
      static double divide(double a, double b) {       return a / b; }
}

class MyMathTest2 {
      public static void main(String args[]) {
            // 클래스메서드 호출
            System.out.println(MyMath2.add(200L, 100L));
            System.out.println(MyMath2.subtract(200L, 100L));
            System.out.println(MyMath2.multiply(200L, 100L));
            System.out.println(MyMath2.divide(200.0, 100.0));

            MyMath2 mm = new MyMath2();
            mm.a = 200L;
            mm.b = 100L;
            // 인스턴스메서드는 객체생성 후에만 호출이 가능함.
            System.out.println(mm.add());
            System.out.println(mm.subtract());
            System.out.println(mm.multiply());
            System.out.println(mm.divide());

}
[실행결과]
300
100
20000
2.0
300
100
20000
2.0

인스턴스메서드인 add(), subtract(), multiply(), divide()는 인스턴스변수인 a와 b만으로도 충분히 원하는 작업이 가능하기 때문에, 매개변수를 필요로 하지 않으므로 괄호()에 매개변수를 선언하지 않았다.
반면에 add(long a, long b), subtract(long a, long b) 등은 인스턴스변수 없이 매개변수만으로 작업을 수행하기 때문에 static을 붙여서 클래스메서드로 선언하였다. MyMathTest2의 main메서드에서 보면, 클래스메서드는 객체생성없이 바로 호출이 가능했고, 인스턴스메서드는 MyMath2클래스의 인스턴스를 생성한 후에야 호출이 가능했다.
이 예제를 통해서 어떤 경우 인스턴스메서드로, 또는 클래스메서드로 선언해야하는지, 그리고 그 차이를 이해하는 것은 매우 중요하다.



3.10 클래스멤버와 인스턴스멤버간의 참조와 호출.


같은 클래스에 속한 멤버들간에는 별도의 인스턴스를 생성하지 않고도 서로 참조 또는 호출이 가능하다. 단, 클래스멤버가 인스턴스멤버를 참조 또는 호출하고자 하는 경우에는 인스턴스를 생성해야 한다.
그 이유는 인스턴스멤버가 존재하는 시점에 클래스멤버는 항상 존재하지만, 클래스멤버가 존재하는 시점에 인스턴스멤버가 항상 존재한다는 것을 보장할 수 없기 때문이다.

[예제6-] ArrayEx.java

class MemberCall {
      int iv = 10;
      static int cv = 20;

      int iv2 = cv;
//   static int cv2 = iv;                   에러. 클래스변수는 인스턴스 변수를 사용할 수 없음.
      static int cv2 = new MemberCall().iv;   // 굳이 사용하려면 이처럼 객체를 생성해야함.

      static void classMethod1() {
            System.out.println(cv);
//         System.out.println(iv);       에러. 클래스메서드에서 인스턴스변수를 바로 사용할 수 없음.
            MemberCall c = new MemberCall();      
            System.out.println(c.iv);   // 객체를 생성한 후에야 인스턴스변수의 참조가 가능함.
     }

      void instanceMethod1() {
            System.out.println(cv);            
            System.out.println(iv);  // 인스턴스메서드에서는 인스턴스변수를 바로 사용가능.
     }

      static void classMethod2() {
            classMethod1();
//         instanceMethod1(); 에러. 클래스메서드에서는 인스턴스메서드를 바로 호출할 수 없음.
            MemberCall c = new MemberCall();
            c.instanceMethod1(); // 인스턴스를 생성한 후에야 인스턴스메서드를 호출할 수 있음.
      }
     
      void instanceMethod2() { // 인스턴스메서드에서는 인스턴스메서드와 클래스메서드
            classMethod1();         // 모두 인스턴스생성없이 바로 호출이 가능하다.
            instanceMethod1();
     }
}

클래스멤버(클래스변수와 클래스메서드)는 언제나 참조 또는 호출이 가능하다.
그렇기 때문에 인스턴스멤버가 클래스멤버를 참조, 호출하는 것은 아무런 문제가 안 된다.
클래스멤버간의 참조 또는 호출 역시 아무런 문제가 없다.

그러나, 인스턴스멤버(인스턴스변수와 인스턴스메서드)는 반드시 객체를 생성한 후에만 참조 또는 호출이 가능하기 때문에 클래스멤버가 인스턴스멤버를 참조, 호출하기 위해서는 객체를 생성하여야 한다.

하지만, 인스턴스멤버간의 호출에는 아무런 문제가 없다. 하나의 인스턴스멤버가 존재한다는 것은 인스턴스가 이미 생성되어있다는 것을 의미하며, 즉 다른 인스턴스멤버들도 모두 존재하기 때문이다.

실제로는 같은 클래스 내에서 클래스멤버가 인스턴스멤버를 참조 또는 호출해야하는 경우는 드물다. 만일 그런 경우가 발생한다면, 인스턴스메서드로 작성해야할 메서드를 클래스메서드로 한 것은 아닌지 한번 더 생각해봐야 한다.

[알아두면 좋아요]

수학에서의 대입법처럼, c = new MemberCall()이므로 c.instanceMethod1();에서 c대신 new MemberCall()을 대입하여 사용할 수 있다.

      MemberCall c = new MemberCall();
      int result = c.instanceMethod1();

위의 두 줄을 다음과 같이 한 줄로 할 수 있다.

      int result = new MemberCall().instanceMethod1();

대신 참조변수를 사용하지 않았기 때문에 생성된 MemeberCall인스턴스는 더 이상 사용할 수 없다

'개발관련 > 자바' 카테고리의 다른 글

java의 for-each 구문 확장  (0) 2010.06.10
[펌]템플릿 형태 JSP  (0) 2010.01.06
[펌]GlassFish와 Tomcat 비교  (0) 2009.06.12
Java 5.x 이상에서 유용한 기능(generic)  (0) 2009.02.27
eclipse DB plugin - DBViewer  (1) 2009.02.27
Posted by 자개비
개발관련/자바2009. 2. 27. 17:48
jdk 1.4 이하에서는 Collection 자료 구조를 사용할 경우 값을 넣을 경우 Object타입으로 들어가서
후에 빼서 사용할 때 (String)과 같이 캐스팅을 해야하는 불편함이 있다. jdk1.5(java 5) 이후부터는 이러한 불편함을
해소할 수 있는 기능이 추가되었다. generics라고 하는 것이다.

예를 들어 ArrayList를 사용할 때 다음과 같이 사용한다.

ArrayList<String> list = new ArrayList<String>();

<String>이라고 parameter를 주면 ArrayList에 들어가는 값은 String 타입이라고 정의해주는 것이다.

이렇게 해서 얻어지는 이점은 일단 값에 대한 타입이 명시적으로 드러나므로 명확한다.

또한 캐스팅이 안되는 값이 들어갈 경우 이전에는 컴파일 단계에서 잡아내지 못하고 실행단계에서 Exception이 나서
잘못 된 것을 알 수 있었는데, 이제는 컴파일 단계에서도 잡아내 주기 때문에 디버깅하기가 수월해졌다.

덧붙여 하나 더 얘기하면, java5 이상에서 Collection의 경우 for문을 간결하게 사용할 수 있게 되었다.

예를 들면,

ArrayList<String> list = new ArrayList<String>(); 와 같이 변수를 선언하고 값을 넣은 후 값을 빼내기 위해서
우리는 for 문을 사용한다.

for(Iterator i = list.iterator(); i.hasNext();) {
  String s = i.next();
  System.out.println(s);
}

아래와 같이 하면 좀 더 간결해진다.

for(String s:list) {
  System.out.println(s);
}

풀이를 하자면, list의 개수만큼 루프를 돌면서 값을 빼서 s에 넣으라는 것이다. 여기서 s는 String으로 선언했다.
list에 들어간 값이 앞에서 <String>으로 했기 때문이다. 그렇지 않다면 Object로 해야 할 것이다.






'개발관련 > 자바' 카테고리의 다른 글

[펌]템플릿 형태 JSP  (0) 2010.01.06
[펌][JAVA] static키워드 바로알기  (0) 2009.12.18
[펌]GlassFish와 Tomcat 비교  (0) 2009.06.12
eclipse DB plugin - DBViewer  (1) 2009.02.27
[펌]Statement vs. PreparedStatement  (0) 2009.02.27
Posted by 자개비
개발관련/자바2009. 2. 27. 09:36

PreparedStatement를 사용하는 경우 분포도
이 부분은 preparedStatement를 사용하는 경우 값의 분포도에 따라 오히려 낮은 성능을 보이는 경우로 자바서비스넷(www.javaservice.net)에서 김주현씨가 답변한 내용을 일부 수정한 것으로 상당히 이해가 쉽게 정리가 되어 있습니다.


 BIND 변수를 쓸 경우 실행계획이 틀어져서 느려지는 그런 경우를 unsafe literal 이라고 합니다.  예를 들어 다음과 같은 쿼리 문이 있다고 하면  select * from emp where empno = :v1  위의 SQL에서 empno 는 emp의 PK입니다. 따라서 preparedStatement를 써서 BIND 변수를 사용하든지 그렇지 않든지 실행계획은 똑같이 나옵니다. 

 

이런 경우를 safe literal 이라고 합니다.  하지만 쿼리 문이 다음과 같다고 하면  select * from emp where deptno > :v1  위의 경우 :v1이 1 일 경우와 1000일 경우는 분포도가 전혀 다릅니다. 1일 경우에는 대부분의 deptno가 1보다는 크기 때문에 분포도가 넓습니다. 즉, 인덱스를 타지 않는게 더 좋습니다. 하지만 deptno가 1000보다 큰 경우는 거의 없습니다.


 

즉, 분포도가 좁고, 이 경우 인덱스를 타야 합니다.  그러니까 히스토그램이라는 것은 이처럼 실제 :v1 값이 무엇이냐에 따라서 분포도가 달라지는 분포도 정보입니다. 어느 value가 어느 정도의 분포도를 가지고 있는지 하는 정보를 히스토그램이라고 합니다.  문제는 BIND 변수를 쓰면 :v1 이 1이 들어올지 1000이 들어올지에 무관하게 무조건 단 하나의 실행계획만을 만들고 일괄적으로 적용하다가 보니 1000이 들어오는데도 불구하고 인덱스를 타지 않고 Full Scan하는 상황이 나올 수 있습니다.  즉, BIND 변수를 활용하면 히스토그램 정보를 활용할 수 없습니다. 


 

그렇다고 prepatedStatement를 쓰지 말아야 할까요? 아닙니다. 써야 합니다. 위와 같이 불충분한 통계정보(히스토그램)이 없어서 실행계획을 잘못 수립해서 느려지는 경우는 어쩔 수 없는 현재의 옵티마이져의 한계로 보아야 합니다. 이 경우 똑똑한 사람이 힌트 등을 통해서 SQL에게 올바른 실행계획을 가르쳐줘야 합니다.  아주 특수한 경우 위와 같은 이유로 튜닝을 위해 Statement를 쓰는 경우도 있습니다.


 

예를 들어 성별이 남, 여 두 가지일 경우, 그리고 남자가 전체의 2%이며 아주 극소수라고 가정하면 남자일 경우에는 인덱스를 타야 하고, 여자일 경우 인덱스를 타지 말아야 합니다. 이 경우 어차피 distinct 값의 종류가 딱 2가지 이므로 BIND 변수를 쓰지 않아서 하드 파싱을 해봤자 2개의 SQL 종류 밖에는 나오지 않으므로 문제가 없습니다.  select * from user where sex = 'm' select * from user where sex = 'f'  오라클의 Shared pool의 Library cache에는 위와 같이 딱 두 종류의 SQL이 저장되어있어서 재사용되겠지요.  


 


Statement vs. PreparedStatement의 성능 차이 


이 부분은 자바서비스넷(www.javaservice.net) 에서 statement와 preparedstatement 중 어떤 것이 나은지 논쟁 중에 박영록(poci)씨가 “static sql과 dynamic sql의 성능 차이”라는 제목으로 preparedstatement를 사용하는 경우 발생할 수 있는 문제에 대해 정리한 내용을 일부 수정한 것으로 소모적인 논쟁 중에서 빛을 발하는 내용입니다.


 prepared statement에서는 보통 변수를 설정하고 바인딩하는 static sql이 사용되고 statement에서는 쿼리 자체에 조건이 들어가는 dynamic sql이 사용됩니다. (주 - 여기서 언급하고 있는 static/dynamic은 일반적으로 얘기하는 동적 sql과는 다른 의미로, 실행계획이 매번 새로 생성되는지 아닌지에 관한 것입니다.)  preparedStatement가 파싱 타임을 줄여주는 것은 분명히 중요한 장점이지만, static sql을 사용하는데 따르는 퍼포먼스 저하를 생각하지 않을 수가 없습니다.  이를테면 이런 예가 있을 수 있습니다.  dynamic sql을 사용하는 경우 자바에서 다음과 같은 SQL을 생성했다고 가정해봅시다. 


 

SELECT ... FROM   TAB1 WHERE  COL1 LIKE '%' AND    COL2 LIKE '%' AND    COL3 LIKE 'ABC%'  그리고 인덱스가 COL1 + COL2 + COL3 로 걸려 있습니다.  그렇다면 실행계획은 당연히 인덱스에서 COL3 컬럼만을 액세스하게 설정될 것입니다.  그런데 다음과 같은 static sql을 봅시다.  SELECT ... FROM   TAB1 WHERE  COL1 LIKE :A || '%' AND    COL2 LIKE :B || '%' AND    COL3 LIKE :C || '%'  이런 식으로 SQL을 작성했다고 합시다.


 

A, B, C에 어떤 값이 들어올지 모르니까 인덱스의 COL1, COL2, COL3 컬럼을 차례대로 스캔하도록 처리 경로가 잡힙니다. 그런데 조건으로 A, B는 NULL, C에는 'ABC'라는 값이 들어왔습니다. COL3 컬럼만 랜덤으로 액세스해서 테이블로 가면 될 것을 인덱스 풀스캔을 해버립니다.  이와 같이 static sql은 변수에 어떤 값이 바인딩 되는지를 알 수 없기 때문에 가장 일반적인 형태로 실행 계획을 작성합니다. 때문에 통계 자료를 제대로 활용할 수 없고 인덱스 활용 등에서 비효율이 발생할 확률이 높아집니다.  이런 예는 정말로 많습니다. 이를테면 TABLE1의 COL1의 분포도가 값이 'A'인 게 100건, 'B'인 게 100만 건 있다고 할 경우, COL1 = :VAR 와 같이 바인딩을 시켜놓으면 늘 풀스캔을 하게 실행계획을 잡습니다.


 

그런데 :VAR에 'A'가 들어가는 경우 인덱스를 타면 금방 결과가 나올 것을 풀스캔 후에 결과를 리턴하게 되죠.  static sql의 활용으로 파싱 타임을 절약해서 매번 실행 계획을 작성하는 것을 피하는 대가로 좀더 비효율적인 실행 계획이 수립된다는 것이죠. 일반적으로 SQL의 실행과정에서 파싱과 실행 계획 수립 과정은 전체 SQL 수행 시간에서 큰 비중을 차지하지 않습니다. 가장 큰 비중으로 차지하는 것은 테이블에서 로우를 가져오는 과정이고 파싱 시간은 이의 10분의 1에 불과합니다. 그런데, 이 10%의 시간을 줄이자고 90%의 시간에서 비효율이 발생하게 만드는 일이 생길 수 있습니다. 


 

물론 쿼리 생성 단계에서부터 어느 정도는 해결할 수 있는 문제입니다만, 그로 인해 자바 코드와 SQL이 복잡하게 얽히는 문제도 결코 작은 문제가 아니죠.  얼마전 대용량 데이터베이스 I, II를 모두 수강했습니다. 강사분께서 계속 강조하는 말씀이 두 가지가 있었습니다. 첫째는 원리를 이해하고 이론적으로 따져보는 것이 중요하다는 것, 그리고 둘째는 이론적으로 따져서 작성한 SQL에 대해 반드시 실행 계획을 떠보라는 것이었습니다. 상반되는 이야기인 것처럼도 보이고 일반적인 이론과 경험의 조화를 말하는 것처럼도 보이지만 저에게는 이론적인 체계를 먼저 잡아나가고 원리를 따져본 다음에 그 이론을 검증해보라는 말로 들렸습니다.  

 

출처 : http://helols.tistory.com/13

'개발관련 > 자바' 카테고리의 다른 글

[펌]템플릿 형태 JSP  (0) 2010.01.06
[펌][JAVA] static키워드 바로알기  (0) 2009.12.18
[펌]GlassFish와 Tomcat 비교  (0) 2009.06.12
Java 5.x 이상에서 유용한 기능(generic)  (0) 2009.02.27
eclipse DB plugin - DBViewer  (1) 2009.02.27
Posted by 자개비