정확한 메시지는 아래와 같음

Description    Resource    Path    Location    Type
The project was not built due to "A resource exists with a different case: '/****'.". Fix the problem, then try refreshing this project and building it since it may be inconsistent    ****        Unknown    Java Problem

Ant로는 컴파일이 되는데,이클립스에서 계속 Problems에 Error라고 표시한다.

찾아보니, 이클립스에서는 패키지 네임을 소문자로 작성해야 한다고 한다. 그리고 Java Spec에서도 찾아보니 아래와 같이 package name은 소문자로 강제하고 있음.

The first component of a unique package name is always written in all-lowercase ASCII letters and should be one of the top level domain names, currently com, edu, gov, mil, net, org, or one of the English two-letter codes identifying countries as specified in ISO Standard 3166, 1981. For more information, refer to the documents stored at ftp://rs.internic.net/rfc, for example, rfc920.txt andrfc1032.txt.

그리고 eclipse에서 svn을 쓰는 사용자들에게 팁. MS Windows의 경우, 파일의 대소문자를 구분하지 않는데 반해, svn에서는 대소문자를 구별하기 때문에, 뭔가 꼬일 가능성이 있다.
그러므로 안전하게 아래와 같은 방법을 따르자.
1. eclipse에서 대문자 package명을 바로 소문자로 바꾸고, 뒤에 의미없는 숫자를 추가하여 refactoring
2. svn commit
3. eclipse에서 아까 추가한 숫자를 다시 제외하도록 refactoring
4. svn commit
끝!

 
description : 기술, 서술적 묘사, 서술, 기재, 서사문
─ tag  Java, 이클립스

요즘 업무 때문에 hadoop을 많이 사용하고 있다. 오늘의 삽질 항목은 hadoop에서 iterator를 재사용할 수 없을까? 하는 것이다. hadoop의 reducer는 동일한 key로 묶인 value들을 가져올 수 있도록, Iterable<?> type을 반환해 준다. 그런데 쓰다 보면, 이 reducer의 값들을 두 번 이상 읽고 싶을 때가 있다. 예를 들면, 전체 value들의 개수를 세어서 값들을 출력할 때 함께 찍어주고 싶다거나.. 기타 max, min 값을 구해서 같이 출력 하고 싶다거나 등 은근 필요한 경우가 많다.

처음에는 단순하게 Iterable<text> values 변수를 복사해 뒀다가, 다시 iterator를 호출해서 쓰는 것을 시도했다. 대충 돌려보니 컴파일 오류 안 나고 실행도 잘 되는 듯 했다! 올레를 외치며 대용량 데이터에 적용을 했는데.. 몇 시간 동안 열심히 작업을 하 더니 내 놓은 결과는,,  reducer output records : 0개  -_- 결과 값이 아무 것도 없는.. T_T

구글링을 해 보니, 구현상의 이유로, reducer의 iterator는 두 번 읽을 수 없다고 한다. (요기 참고) 생각해보면, key에 딸린 value들이 어마어마하게 많으면, 파일 블록 하나로 처리가 안될 테니, 여러 파일에서 나눠서 읽어야 할 테고, 여기서 iterator를 다시 호출 할 수 있게 하려면 시스템이 복잡해 질 수도 있겠다 싶었다. (그래도 좀 해주면 안 되려나)

하여간, 이를 극복하기 위해서는,

  1. value들을 모두 list에 따로 저장
  2. value들을 local file에 저장했다 다시 읽기

요 두 가지 방법 정도가 떠올랐다. 처음에 list에 저장하도록 해서 돌렸더니, 여기저기서 out of memory 오류를 뿌리며 죽었다. –_-; 은근 메모리에 다 못 올릴 cluster들이 많은가 보다.

그래서 local file에 저장해서 다시 읽을까 했는데, 작은 cluster에 대해서도 일일이 local file에 저장하고 읽으면 이것도 은근 속도에 bottleneck이 될 듯 하여.. n개 이하는 메모리의 list로 처리하고, n개가 넘어가면, 파일에 저장해서 다시 읽도록 구현해서 돌렸다. 그런데 이게 어디서 오류가 생긴건지 24시간이 지나도록 몇몇 reducer 작업들이 끝나지를 않는다. –_-; 아놔

일단 시간이 없어서, 결국 이번에는 iterator를 한 번 사용해서 결과를 찍고, 결과를 별도로 후처리를 하여 원하는 값을 얻었다. 뭔가 더 깔끔한 방법이 있을 듯 한데,, 방법이 없을까?

─ tag  hadoop, Java, 하둡
사실 사용법이라기 보다는, TreeSet을 사용할 때 유의할 점 정도가 맞겠다.

HashSet을 사용하기 위해서는 HashSet에 들어가는 원소의 equals() 함수와 hashCode() 함수를 각각 override해줘야 한다. 그럼 TreeSet을 사용하기 위해서는?

Oracle이 제공하는 Java Doc 문서를 살펴보면, 다음과 같이 나와있다.
This is so because the Set interface is defined in terms of the equals operation, but a TreeSet instance performs all element comparisons using its compareTo (or compare) method, so two elements that are deemed equal by this method are, from the standpoint of the set, equal. 
==> Set 인터페이스는 equals() 함수를 통하여 정의되어 있는데, TreeSet은 모든 원소의 비교를 compareTo() 혹은 compare() 함수를 통해서 수행하기 때문에, set의 관점에서 봤을 때, 두 원소들이 compareTo() 함수에 의해 같다고 생각되면, 동일해야 한다.
출처 : 
http://download.oracle.com/javase/6/docs/api/java/util/TreeSet.html
즉, TreeSet은 원소의 compareTo() 함수만 잘 구현해주면, 정렬된 상태로 잘 저장이 된다는 말씀! 실제로 TreeSet 예제를 찾아보면, compareTo() 함수만 override 한 예제도 많다. 그리고 equals()가 참일 때, compareTo() 함수가 0을 반환하도록만 신경 써 주면 별 문제없이 잘 동작한다.

그럼 이제 주의해야 할 점! 아래 코드를 보자.
import java.util.Set;
import java.util.TreeSet;


public class Test {
	public static class Data implements Comparable<Data>{
		private String id = null;
		private int priority = 0;
		public Data(String id, int priority){
			this.id = id;
			this.priority = priority;
		}
		@Override
		public int compareTo(Data op){	
			System.out.printf("%s vs %s\n", this, op);
			// id가 동일하면 동일한 객체
			if( id.equals( op.id ) ){
				return 0;
			}			
			if( priority != op.priority ){
				if( priority > op.priority ){
					return 1;
				}
				else{
					return -1;
				}
			}
			return id.compareTo( op.id );			
		}
		@Override
		public String toString(){
			return String.format("%s/%d", id, priority);
		}	
		
	}
	public static void main(String[] args){
		Set<Data> treeSet = new TreeSet<Data>();		
		
		treeSet.add( new Data("a", 0) );
		treeSet.add( new Data("b", 1) );
		treeSet.add( new Data("c", 2) );
		treeSet.add( new Data("a", 3) );
		
		for(Data d : treeSet ){
			System.out.println( d );
		}		
	}

}
위 코드는 심플하다. TreeSet에 들어가는 Data class는 String id를 통해 객체의 동일 여부를 판별한다. 그리고 id가 다르면, int priority 변수를 통해 정렬된다. 이 코드를 실행 결과는 어떻게 될까? 상식적으로 판단하면, 마지막에 추가된 a/3 객체는 a/1 객체와 동일한 객체이기 때문에 TreeSet에 추가되면 안된다. 하지만 결과는 다음과 같다.

== 실행 결과==
a/0
b/1
c/2
a/3
==========

a/3가 추가되었으므로, 원하는 결과와는 다르다. equal()와 hashCode()를 추가해줘야 할까? 다음과 같이 두 함수를 추가해보자.
import java.util.Set;
import java.util.TreeSet;

public class Test {
	public static class Data implements Comparable<Data>{
		private String id = null;
		private int priority = 0;
		public Data(String id, int priority){
			this.id = id;
			this.priority = priority;
		}
		@Override
		public int compareTo(Data op){	
			System.out.printf("%s vs %s\n", this, op);
			// id가 동일하면 동일한 객체
			if( id.equals( op.id ) ){
				return 0;
			}			
			if( priority != op.priority ){
				if( priority > op.priority ){
					return 1;
				}
				else{
					return -1;
				}
			}
			return id.compareTo( op.id );			
		}
		@Override
		public String toString(){
			return String.format("%s/%d", id, priority);
		}	
		@Override
		public int hashCode(){
			return id.hashCode();
		}
		@Override
		public boolean equals(Object o){
			if( o == null)
				return false;
			if( !( o instanceof Data) ){
				return false;
			}
			Data op = (Data) o;
			if( this == op ){
				return true;
			}
			return id.equals( op.id );
		}
	}
	public static void main(String[] args){
		Set<Data> treeSet = new TreeSet<Data>();		
		treeSet.add( new Data("a", 0) );
		treeSet.add( new Data("b", 1) );
		treeSet.add( new Data("c", 2) );
		treeSet.add( new Data("a", 3) );
		
		for(Data d : treeSet ){
			System.out.println( d );
		}		
	}

}

하지만 여전히 실행결과는 다음과 같다.

== 실행 결과==
a/0
b/1
c/2
a/3
==========

무엇이 문제일까? 추측컨데, TreeSet은 내부적으로 tree 형태로 데이터를 저장할 것이다. a/0, b/1, c/2 객체가 추가되었을 때, well balanced binary tree 형태로 생각해보면, b/1가 tree의 root 이고, a/0와 c/2는 각각의 child node일 것이다. 여기에 새로운 a/3가 추가되었을 때, 이 a의 priority는 3이므로, 처음에 b/1과 compareTo() 함수가 실행되고, 그리고 다음으로는 c/2와 compareTo() 함수가 실행될 것이다. (실제 compareTo() 에 로그를 남기면 추측이 맞음을 알 수 있다.)

즉, 마지막에 추가된 a/3 객체는 a/0 객체와는 아예 비교가 일어나지 않으므로, 문제가 발생한다. 의미적으로 보면 동일한 두 객체인 a/3 객체와 a/0 객체가 모두 Set에 존재하는 상황이 발생하는 것이다.

그렇다면 어떻게 해야할까?

일단 나는 Java 내공이 부족한 관계로, TreeSet 외에 HashSet을 추가하여, TreeSet에 Data를 넣고 빼기 전에 HashSet을 통해 검증하는 방법을 택했다. 이게 맞는 방법인지는 모르겠지만, 일단은 제일 무난한 방법. 혹시 다른 해법이 있으신 분은 제보 바랍니다!




─ tag  Java, set, TreeSet

팀에서 요즘 이펙티브 자바 스터디를 진행하고 있다. 한 주에 두 번 스터디를 하고, 한 번 할 때 세 item씩 진행한다. 뭐 그정도야.. 라고 생각할 수도 있겠지만, Java의 기초가 얕은 나로써는 꽤나 하드 트레이닝이다.

C++를 기준으로 대충 찍어 맞추는 것도 슬슬 한계가 보이고, 기본적으로 Java 문법을 알고 있다는 가정 하에, 효율적인 Java 프로그래밍을 하기 위해서는 이렇게 해야 한다.. 라고 소개 하는 책인데, C++에 없는 Java 문법이 나오면 헷갈리기 시작하여 그 item은 통으로 딴나라 이야기가 되어 버리곤 한다.

이럴 땐 다시 기본 Java 책(난 Thinking In Java를 보고 있는데, 이 책은 진짜 완전 교과서T.T )으로 돌아가 해당 문법을 대강 파악한 다음, 다시 Effective Java 책을 봐야 하는데, 다 좋은데 시간이 너무 많이 걸린다. –.-;; 이거 직장인인데 점점 학생 모드가 되어가네.

'Diary' 카테고리의 다른 글

단어 선택의 차이  (0) 2010.02.22
연극 라이어2탄을 보고  (0) 2010.02.20
이펙티브 자바 스터디  (0) 2010.02.18
설날, 생일, 발렌타인 데이  (4) 2010.02.14
인공 치아의 사나이  (2) 2010.02.12
우리 팀 문화  (4) 2010.02.12
─ tag  Java, 스터디

많은 사람들이 Java와 JavaScript가 비슷한 언어 아니냐.. 혹은 JavaScript가 Java에서 파생된 언어가 아니냐 라고 오해를 하셔서 정리를 좀 해 보았습니다.

JavaScript의 기원은 넷스케이프사가 개발한 서버측 컴포넌트와 연동할 수 있는 클라이언트측의 스크립트 언어입니다. 당시의 이름은 라이브 스크립트였고, 이후 넷스케이프사는 Java 프로그래밍 언어의 소유권을 가진 썬사와 제휴를 맺었으며, 그 후 넷스케이프사의 엔지니어가 라이브스크립트를 ‘자바스크립트’라는 이름으로 변경을 하였다고 합니다. 어디까지나 마케팅 측면을 고려하여 이미 유명세가 있던 Java의 이름을 본따 ‘자바스크립트’라는 이름을 지었을 뿐이지, 사실 두 언어 사이에는 아무런 관련성이 없습니다.

발췌 및 정리 from 자바스크립트 for 웹 2.0 / 한빛미디어

─ tag  Java, JavaScript
openclose

티스토리 툴바