in JAVA

JAVA – 자주하는 실수 10가지

원문

이 글은 자바 개발자들이 자주하는 실수 10가지에 대한 글입니다.

1. Array 객체를 ArrayList로 변환하기

아래와 같이 자주들 이렇게 사용하고는 합니다:

List<String> list = Arrays.asList(arr);

하지만! Arrays.asList()메서드는 Arrays클래스 내부에 있는 private static class 객체를 리턴합니다. java.util.ArrayList class가 아니라는 말이죠.
java.util.Arrays.ArrayList 클래스는 set(), get(), contains() 메서드를 가지며 대신 그외 element를 추가하기 위한 아무 메서드도 지원하지 않습니다. 그래서 리스트의 사이즈는 항상 일정합니다.

그럼 어떻게 사용해야 할까요?

ArrayList<String> arrayList = new ArrayList<String>(Arrays.asList(arr));

ArrayList 생성자는 Collection type의 객체를 받을 수 있고, 이는 java.util.Arrays.ArrayList가 됩니다.

2. 만약 Array 객체에서 특정 값을 찾는다면?

개발자들은 아래와 같이 자주 사용하고는 합니다.

Set<String> set = new HashSet<String>(Arrays.asList(arr));
return set.contains(targetValue);

위 코드는 정상적인 작동을 합니다. 하지만 구지 List를 Set으로 변환 할 필요는 없습니다. 이는 추가적인 시간 비용이 들기 때문에 아래와 같이 간단하게 해결 할 수 있습니다.

Arrays.asList(arr).contains(targetValue);

또는

for(String s: arr){
    if(s.equals(targetValue))
        return true;
}
return false;

첫 번째 방법이 두번째 방법보다 읽기가 편하겠죠?

3. 루프 상태에서 List의 특정 객체 지우기

아래의 코드를 돌리다면 어떠한 일들이 벌어질까요? 한번 생각해봅시다.

ArrayList<String> list = new ArrayList<String>(Arrays.asList("a", "b", "c", "d"));
for (int i = 0; i < list.size(); i++) {
    list.remove(i);
}
System.out.println(list);

결과는 처참합니다.

[b, d]

위 코드는 심각한 문제를 가지고 있습니다. 특정 객체를 지우게 되면, 리스트의 사이즈는 줄어들게 되고, 삭제할 타겟의 인덱스 또한 변하게 됩니다. 그래서 만약 루프 안에서 인덱스를 사용하여 여러개의 Element를 지우게 한다면 정상적으로 작동하지 않게 됩니다.

아마 알지도 모르겠지만 대안은 iterator를 사용하면 됩니다. 그리고 foreach문이 있겠죠. 자바에서 foreach문은 iterator처럼 작동하지만 사실 iterator가 아닙니다.

아래의 코드를 보도록 하죠.

ArrayList<String> list = new ArrayList<String>(Arrays.asList("a", "b", "c", "d"));
 
for (String s : list) {
    if (s.equals("a"))
        list.remove(s);
}

위 코드는 ConcurrentModificationException를 발생시킵니다.

하지만 아래의 코드는 정상적으로 작동합니다.

ArrayList<String> list = new ArrayList<String>(Arrays.asList("a", "b", "c", "d"));
Iterator<String> iter = list.iterator();
while (iter.hasNext()) {
    String s = iter.next();
 
    if (s.equals("a")) {
        iter.remove();
    }
}

.next() 메서드는 .remove() 메서드가 호출되기 전에 호출되어야 합니다. foreach문에서 ConcurrentModificationException 문제의 원인은 Compiler.remove() 호출 후에 .next()가 불리도록 만들기 때문입니다.

자세한 내용은 소스코드를 참조해주세요.

4. Hashtable vs HashMap

알고리즘 관례에서, Hashtable은 데이터 구조의 이름이라고 한다고 하네요. 하지만 자바에서는 데이터 구조 이름은 HashMap이라고 합니다.

여기서 중요한 차이점 하나는 Hashtable은 동시성을 가진다는 것과 HashMap는 그렇지 않다는 것입니다.

그래서 Hashtable보다 HashMap의 사용을 많이 합니다.

HashMap vs. TreeMap vs. Hashtable vs. LinkedHashMap
Top 10 questions about Map

5. Collection의 Row Type을 사용해라!?

자바에서, raw type과 unbounded wildcard type은 같이 사용하기 쉽습니다. Set이 raw type이고, Set이 unbounded wildcard type입니다.

파라미터로 raw type의 List객체를 사용하는 코드를 생각해봅시다.

public static void add(List list, Object o){
    list.add(o);
}
public static void main(String[] args){
    List<String> list = new ArrayList<String>();
    add(list, 10);
    String s = list.get(0);
}

이 코드는 아래와 같이 예외를 발생하게 하는데,

Exception in thread "main" java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
    at ...

여기서 **Set,Set,그리고 **Set는 몇가지 차이점이 있습니다.

자세한건 Raw type vs. Unbounded wildcardType Erasure를 참조해주세요.

6. 접근 제어

개발자들은 자주 public 클래스 필드를 사용합니다. 필드에 대한 접근이 다이렉트로 이루어지기 때문에 매우 쉽기는 하지만 클래스 디자인 측면에서는 좋지 않습니다. 접근 제어에 대해서는 적으면 적을 수록 좋은 디자인입니다.

자세한 내용은 아래의 링크를 참조해주세요.
public, default, protected, and private

7. ArrayList vs. LinkedList

ArrayListLinkedList의 차이점을 잘 모르는 개발자들은 주로 ArrayList를 사용하고는 합니다. 그 이유는 매우 친숙하기 때문이죠. 그렇지만, 여기에는 아주 큰 퍼포먼스 차이가 있습니다.

LinkedList의 경우 추가와 삭제가 빈번하게 사용될때 사용되어야 하며, 또한 random access 동작이 많지 않습니다.

좀더 상세한 정보를 얻기 위해서 ArrayList vs. LinkedList을 참조해주세요.

8. Mutable vs. Immutable

Immutable 객체는 단순함, 안전함, 기타 등등의 여러가지의 혜택을 받습니다. 하지만 제한된 값을 떼어낼때, 객체들에 많은 변화가 필요할때 가비지 컬렉션이 자주 일어나게 되는데요, 이럴대는 발란스를 맞춰 선택을 해야합니다. 내가 mutable을 사용할지 아니면 immutable을 사용할지에 대해서 말입니다.

일반적으로, mutable객체는 많은 객체의 생상을 피하기 위해 사용되는데요, 아주 오래된 예로 문자열의 결합을 말할 수 있을 있을거 같습니다. 만약 immutable string을 사용한다면, 아무래도 많은 객체 생성과 그에 합당한 가비지 컬렉션이 일어나겠죠. 이는 시간과 cpu 전력을 낭비하는 일입니다. 이럴때는 간단하면 mutable 객체(e.g. StringBuilder)를 사용하면 됩니다.

String result="";
for(String s: arr){
    result = result + s;
}

mutable객체가 매력적으로 보일때는 이런 상황입니다. 예로, 메서드 호출로 하나의 값이 아닌 여러개의 값을 얻고자 할때, 그리고 정렬이나, 필터링 등이 될수 있습니다. 물론 제대로 사용하지 않으면 많은 비용을 그만큼 지불하게 되겠죠.

자세한 내용은 아래의 링크를 참조해 주세요.
from dasblinkenlight’s answer on stack overflow

Why String is Immutable?

9. Super와 Sub의 생성자

Implicit-super-constructor-is-undefined-for-default-constructor

기본 슈퍼 생성자는 정의되어 있지 않아 에러가 발생한다. 자바에서 만약 생성자를 생성하지 않으면, 컴파일러는 인자를 받지 않는 기본생성자를 기본으로 생성한다.

만약 슈퍼 클래스에 생성자가 정의되어 있다면, 위와 같은 경우 Super(String s), 컴파일러는 파라미터가 없는 생성자를 만들지 않을 것이다.

그 파라미터가 있거나 또는 없는 서브 클래스의 생성자는 파라미터가 없는 슈퍼 생성자를 호출한다.
컴파일러는 서브 클래스에 super() 생성자를 추가할려고 할것이고, 그렇지만 그 슈퍼의 기본 생성자는 정의 되어 있지 않다. 그래서 컴파일러는 에러를 표시할 것이다.

이 문제를 해결하기 위해서는 간단하게 Super()생성자를 생성하는것이다.

public Super(){
    System.out.println("Super");
}

또는 슈퍼 생성자를 지우고 서브 생성자를 생성하는 것이다.

Constructor of Super and Sub

10. “”or 생성자?

문자열은 아래와 같이 두가지 방식으로 생성될 수 있다.

//1. use double quotes
String x = "abc";
//2. use constructor
String y = new String("abc");

어떤 차이점이 있을까?

아래의 코드로 빠르게 확인 할 수 있다.

String a = "abcd";
String b = "abcd";
System.out.println(a == b);  // True
System.out.println(a.equals(b)); // True
 
String c = new String("abcd");
String d = new String("abcd");
System.out.println(c == d);  // False
System.out.println(c.equals(d)); // True

좀 더 자세한 내용을 알기 위해서는 아래의 링크로 확인 할 수 있다.
Create Java String Using ” ” or Constructor?.

내일을 향해
위 목록들은 GitHub, Stack Overflow의 open source project의 분석으로 통한 질문으로, 구글 인기 검색이다. 탑 10에 대한 정확한 평가, 수치는 없지만 공통적으로 많이 언급이 되었던 부분이다.

이상

Write a Comment

Comment