Python에는 Iterable한 항목을 감싸서 메모리 사용량을 최소로 하면서, 인덱스가 포함된 튜플을 돌려주는, enumerate라는 훌륭한 built-in function이 있다. Java에도 있으면 좋을 것 같아서, 흉내 내봤다.

// coded by jong10
// Enumerator class, version:20090630

import java.util.Collection;
import java.util.Iterator;
import java.util.NoSuchElementException;

public class Enumerator<T> implements Iterable<Enumerator.Entry<T>> {
	public static class Entry<T> {
		private T value;
		private int index;

		Entry(int index, T value) {
			this.index = index;
			this.value = value;
		}

		public int getIndex() {
			return index;
		}

		public T getValue() {
			return value;
		}
	}

	private static class EnumeratorIterator<T> implements Iterator<Enumerator.Entry<T>> {
		private int lastIndex = 0;
		private Iterator<T> iterator;
		
		public EnumeratorIterator(Iterator<T> iter) {
			this.iterator = iter;
		}
		
		public boolean hasNext() {
			return iterator.hasNext();
		}
		
		public Enumerator.Entry<T> next() {
			if (hasNext() == false)
				throw new NoSuchElementException();
			
			return new Enumerator.Entry<T>(lastIndex++, iterator.next());
		}
	
		public void remove() {
			iterator.remove();
		}
	}

	private EnumeratorIterator<T> iterator;

	public Enumerator(Collection<T> c) {
		this.iterator = new EnumeratorIterator<T>(c.iterator());
	}

	@Override
	public Iterator<Enumerator.Entry<T>> iterator() {
		return iterator;
	}
}

사용 방법을 보여주는 JUnit 테스트케이스이다.

@Test
public void testEnumerator() {
	List<Character> list = Arrays.asList(new Character[] {'h', 'e', 'l', 'l', 'o'});
	
	int[] indexes = new int[list.size()];
	char[] values = new char[list.size()];
	
	for (Entry<Character> entry : new Enumerator<Character>(list)) {
		indexes[entry.getIndex()] = entry.getIndex();
		values[entry.getIndex()] = entry.getValue();
	}
	
	assertArrayEquals(new int[] {0, 1, 2, 3, 4}, indexes);
	assertArrayEquals(new char[] {'h', 'e', 'l', 'l', 'o'}, values);
}

자바도 제네레이터가 있으면 좋을텐데…

-- 이상한 나라의 종텐.


(1) 아래 코드는 Java의 메소드다. 무엇이 잘못 되었나?

void set(String s) {
	s = "jong10";
}

(2) 아래 코드는 C++이다. 이 놈은 위와 뭐가 다른가?

void set(string& s) {
	s = "jong10";
}

그냥 별 생각 없이 코딩을 하다간, (이것보다 코드가 훨씬 길고 복잡한 상황에서) 첫 번째 코드의 실수를 하긴 매우 쉽다. 하지만 그와 반대로, C++을 오랫동안 하지 않고, Java, C#, Python, Ruby, Javascript 등에 익숙해지면, 두 번째 코드와 첫 번째 코드의 차이점을 기억하기란 더 어렵다. -_-;; C++의 reference 변수를 이용한 매개변수 전달을 Call by reference라고 하는 것이 틀린 표현은 아니지만, 그냥 alias라고 하는 편이 좀 덜 헷갈릴 것 같다. 나만 그런가? -_-)a

-- 이상한 나라의 종텐.


알량한 자바 지식으로, 예외처리를 어댑터 패턴으로 묶는 중에, 기존의 예외를 어떻게 해야 버리지 않고 stack trace에 반영할 수 있을지 고민을 하다보니, Chained Exceptions라는 방법이 있더라.

위 글에 나온 코드를 간단히 바꿔서 붙여보면,

class MyException extends Exception{
	public MyException() {
	}
	public MyException(String message){
		super(message);
	}
	public MyException(Throwable throwable){
		super(throwable);
	}
	public MyException(String message, Throwable throwable) {
		super(message, throwable);
	}
}

요러코롬 되어있으면, 아래처럼 wrapping하고,

try {
	// blah blah
} catch (UnknownHostException e) {
	throw new MyException("cannot open socket", e);
}

이 메소드를 사용하는 바깥에서는, MyException만을 받아주면 된다. MyException의 생성자를 저렇게 해놓으면, printStackTrace()를 찍었을 때에, 그 속에 있던 익셉션도 스택 트레이싱이 된다. 다른 메소드들은 전혀 오버라이딩 할 필요가 없다. 나는 잘못했었구나! 무식하면 손발이 고생이다. 엎어야겠다.

-- 이상한 나라의 종텐.


Java NIO

2009/04/20 02:08

학교에서 진행하는 팀 프로젝트에서, RMI를 사용할지, 옛날 Socket을 사용할지, NIO를 사용할지 고민중이다. 옛날 소켓과 RMI는 꺼려지는 부분들이 각각 있고, RMI-IIOP는 오버스펙이다. 프로젝트의 목적을 고려하면 NIO가 가장 적절한 것 같은데, 작업량이 꽤 많아질 것 같아서 일정이 도저히 자신이 없다. 아키텍쳐로 보나 성능으로 보나, NIO가 가장 적절한 선택인데.

일단, NIO를 고려하면서, 이것저것 찾아보고 있다. 이런 상황에 닥칠때마다, (내공도 없지만서도) 평소에 외공을 좀 착실히 쌓아뒀어야 하는건데 싶다. NIO를 다뤄봤으면 시간적 부담이 안될텐데.

-- 이상한 나라의 종텐.


Annotations in Java 5

2009/04/18 15:00

어노테이션은 Java 6 에서 추가된건줄 알고 있었는데, 5부터 있었구나. 근데, 왜 어노테이션을 설명해주는 자바 문법 책은 거의 없는거지? -_-+

그나저나, Dolphin(Java 7)은 대체 언제 나오는거야?! 첨자 연산자 재정의와, 멍청해보이는 getter, setter들을 대체할 Property랑, 무엇보다도 병맛 나는 익명 클래스를 대체할 Closure를 쓰고 싶은데… (C++, Java, C#에서 2009~2010년에 추가될 예정인 기능들이 1960년대의 Lisp에서 나온것임을 생각해보면, 폴 그레이엄 아저씨의 Lisp를 예찬하던 글이 떠오른다.)

-- 이상한 나라의 종텐.


싱글턴과 동기화

2008/12/28 15:15

싱글턴을 구현하다가, Head First Design Patterns에서 싱글턴에 관한 문제점을 읽었던 기억이 나서, 책을 뒤적여봤다. 일반적으로 Singleton은 다음과 같이 구현한다.

public class Singleton {
	private static Singleton uniqueInstance;
	// 기타 인스턴스 변수..
	private Singleton() {}
	public static Singleton getInstance() {
		if (unuqueInstance == null) {
			uniqueInstance = new Singleton();
		}
		return uniqueInstance;
	}
	// 기타 메소드..
}

하지만, 위 방법은 멀티쓰레딩에서 문제점이 있다. if문 안쪽 부분이 동기화가 되지 않으면, 인스턴스가 두개 이상 생성될 수 있다. 가장 간단한 해결책은 getInstance를 synchronized로 선언하는 것이지만, 속도가 수십배 느려질 수 있다. (책의 주장에는 메소드를 통째로 동기화하면, 100배까지 느려질 수도 있다고 한다.)

세가지 해결책이 있다.

  1. getInstance()의 속도가 그리 중요하지 않으면, 그냥 synchronized로 한다.
  2. 인스턴스를 lazy binding을 하지 말고, 처음부터 만든다.
  3. DCL(Double Checking Locking)을 써서 동기화 되는 부분을 줄인다.

2번은 그냥 위 코드에서, uniqueInstance를 선언할 때에 인스턴스를 생성하란 소리다.

public class Singleton {
	private static Singleton uniqueInstance = new Singleton();
	// 생략..

책임을 JVM에게 넘기게 되고, 코드는 가장 깔끔하긴 한데, 일단 사용되지 전에도 인스턴스를 차지하고 있고, 몇몇 문제들이 있다고 한다. 다음은 DCL을 쓰는 방법.

public class Singleton {
	private volatile static Singleton uniqueInstance;
	// 기타 인스턴스 변수..
	private Singleton() {}
	public static Singleton getInstance() {
		if (unuqueInstance == null) {
			synchronized (Singleton.class) {
				if (unuqueInstance == null) {
					uniqueInstance = new Singleton();
				}
			}
		}
		return uniqueInstance;
	}
	// 기타 메소드..
}

당연하지만, 안쪽의 if문을 없애면 안된다. 인스턴스가 두개 이상 생성될 수 있다. 그래서 Double Checking이다. 반면, 성능은 1번에 비해서 빠르고, 2번의 단점도 없다. 타이핑 하기 좀 귀찮다는 것 정도? =_=;

1~3번 중에 입맛대로 골라쓰면 되겠다. 코드의 출처는 Head First Design Patterns이다.


자바 성능을 결정짓는 코딩 습관과 튜닝 이야기미리 말해두자면, 나는 자바랑 별로 친하지 않다. 자바는 2008년 지금을 기준으로 세상을 움직이고 있는 가장 큰 플랫폼임에 분명하므로, 좀 더 친해지려고 노력중이기는 한데, 그 많은 Design Patterns, J2EE Design Patterns, JSP & Servlet, EJB, Spring, Struts, Hibernate 등등 자바의 커버리지가 워낙 넓다보니, "어느 세월에 이걸 다~ 공부한담?"하는 생각도 든다. 자바만 할 것도 아니고 말이지.

여튼, 자바랑 좀 더 친해질 수 있을 것 같다는 알량한 마음가짐으로 이 책을 소설책 보듯이 날림으로 읽었는데, 이 책은 J2EE 디자인 패턴, GC의 원리 및 종류, 프로파일링, StringBuilder, 컬렉션, reflection, synchronized, IO & NIO, 로깅, jsp/servlet, DB, XML 등을 사용할 때의 문제점들과, 각종 튜닝 방법들에 대해 쉽게 설명한 좋은 책이다.

한 문장으로 설명하자면, 다양한 토픽들에 대해 성능과 관련된 문제점과 해결책 등을 다루는 책이라 할 수 있다. 성능 그게 뭐 얼마나 중요하냐 하겠냐만은, 클릭 할때마다 5초가 걸리던 페이지가, 사소한 부분을 변경했더니 0.03초로 줄어든다던가.. 그런 일들이 세상에는 많이 벌어지고 있다. 이런 무지한 코드의 원인은 무지한 개발자이니, 다함께 이런 책을 읽고, 속 터질 정도로 느리지 않은, 2~3일만에 뻗어버리지 않는, 그런 시스템을 만들어야 하지 않겠는가? (웃음)

Blog2Book 시리즈 자체가, 블로그 읽듯이 쉽게쉽게 읽을 수 있는 방식으로 씌여져 있기도 하고, 내가 책을 읽는 스타일이 씹지도 않고 삼켜버리는 타입이라, 소화되지 못한 부분도 있지만, 소화된 부분들은 상당한 도움이 되었다. 소화하지 못한 부분들은 자바에 좀 더 경험을 쌓고, 나중에 다시 읽어봐야겠다. 국내 저자가 쓴 책이고, 쉽게 읽히며, 내용도 좋으니, 별은 4개. ★★★★

-- Jong10


Jython, 일원짜리 팁들

2008/05/27 00:21

Jython

요 며칠간 삽질을 한 소소한 기록이다. 기록은 기억을 지배하므로 남겨둔다.

jythonc로 컴파일(!)하면, JVM용 클래스 파일로 만들 수 있다.

  • 다만, jythonc는 jython보다 버전이 늦게 올라 가는 듯?
  • 자바에서 사용할 메소드들에는 "@sig public int getNumber(boolean flag)" 와 같은 표시를 해준다.
  • jythonc로 컴파일 할 때에, -all 옵션을 주면, 여기저기 잘 붙는다.

Jython에는 unsigned가 없다.

  • Jython 자체가 Java로 만들어졌고, JVM 위에서 돌아가기 때문인듯.
  • array()에서 'B' 와 같은 unsigned byte도 당연히 지원되지 않는다.

Jython에서 만든 클래스를 Java에서 사용할 때,

  • 생성자에 매개변수가 없으면 @sig를 안써도 된다. (1개 이상 있으면 써야함.)
  • primitive type의 배열은 자이썬에서 리턴하고, 자바에서 받을 수 있다.
  • Jython에서 String[]을 리턴할 방법은 없지만, ArrayList에 add()한 후에 리턴하면 된다.
  • String의 인코딩은 UTF-8 인듯. (아마도..) 어차피 영문은 상관없다.

hex()의 결과는, Jython과 Python이 다르다.

  • hex(-129)를 하면, Jython에서는 "0xffffff7f"가 나오고, Python에서는 "-0x81"이 나온다.
  • 하지만, 사실 파이썬도 내부적으로는 0xffffff7f와 같은 2의 보수 형태로 저장한다. 2의 보수에서만 가능한 몇가지 성질이 그대로 적용된다.
  • 즉, 파이썬이나 자이썬이나 음수에 대한 자료 저장 형식은 같지만, hex()를 하거나, "%X" % 숫자 등의 방법으로 16진수로 표현된 문자열로 변환하면 결과가 다르게 나온다. 자이썬은 그대로를 문자열로 옮겨주는 경우이고, 파이썬은 숫자만 먼저 바꾼 후에 부호를 나중에 붙이는 방식이다. 일장일단이 있다.

-- Jong10


p.s.1. 모니터 경품이 받고 싶어서 글 올리는거 아니에요 ㅋㅋㅋㅋㅋ
p.s.2. 아마 95% 정도의 확률로 가게 될 듯~~