ITEM 6: Avoid Unnecessary Object

λ˜‘κ°™μ€ κΈ°λŠ₯의 객체λ₯Ό 맀번 μƒμ„±ν•˜λŠ” 것보닀 객체 ν•˜λ‚˜λ₯Ό μƒμ„±ν•˜μ—¬ μž¬μ‚¬μš©ν•˜λŠ” 편이 쒋을 λ•Œκ°€ λ§Žλ‹€. 특히 λΆˆλ³€ 객체(item 17)λŠ” μ–Έμ œλ“ μ§€ μž¬μ‚¬μš©ν•  수 μžˆλ‹€.

// μ•ˆμ’‹μ€ 예 - 호좜될 λ•Œλ§ˆλ‹€ μΈμŠ€ν„΄μŠ€ μƒˆλ‘œ 생성
String s = new String("bad example");

μœ„μ˜ λ¬Έμž₯은 싀행될 λ•Œλ§ˆλ‹€ String μΈμŠ€ν„΄μŠ€λ₯Ό μƒˆλ‘œ λ§Œλ“€λ©°, 이 λ¬Έμž₯이 λ°˜λ³΅λ¬Έμ΄λ‚˜ 빈번히 ν˜ΈμΆœλ˜λŠ” λ©”μ„œλ“œ μ•ˆμ— μžˆλ‹€λ©΄, String μΈμŠ€ν„΄μŠ€κ°€ μˆ˜μ—†μ΄ 많이 λ§Œλ“€μ–΄ 질 수 μžˆλ‹€.

// ν•˜λ‚˜μ˜ String μΈμŠ€ν„΄μŠ€ μ‚¬μš©
String s = "good example";

μœ„μ˜ 경우 μƒˆλ‘œμš΄ μΈμŠ€ν„΄μŠ€λ₯Ό 맀번 λ§Œλ“œλŠ” λŒ€μ‹  ν•˜λ‚˜μ˜ μΈμŠ€ν„΄μŠ€λ₯Ό μ‚¬μš©ν•˜λ©°, 같은 가상 λ¨Έμ‹  μ•ˆμ—μ„œ λ˜‘κ°™μ€ λ¬Έμžμ—΄ λ¦΄ν„°λŸ΄μ„ μ‚¬μš©ν•˜λŠ” 경우 λͺ¨λ“  μ½”λ“œκ°€ 같은 객체λ₯Ό μž¬μ‚¬μš©ν•¨μ΄ 보μž₯λœλ‹€.

정적 νŒ©ν„°λ¦¬ λ©”μ„œλ“œ-item1λ₯Ό μ œκ³΅ν•˜λŠ” λΆˆλ³€ ν΄λž˜μŠ€μ—μ„œλŠ” λΆˆν•„μš”ν•œ 객체 생성을 ν”Όν•  수 μžˆλ‹€.

// μƒμ„±μž - Java9μ—μ„œ deprecated
public Boolean(String s) {
  this(parseBoolean(s));
}
// νŒ©ν„°λ¦¬ λ©”μ„œλ“œ
public static Boolean valueOf(String s) {
  return parseBoolean(s) ? TRUE : FALSE;
}

μƒμ„±μžλŠ” 맀번 μƒˆλ‘œμš΄ 객체λ₯Ό μƒμ„±ν•˜μ§€λ§Œ, νŒ©ν„°λ¦¬ λ©”μ„œλ“œλŠ” 그렇지 μ•ŠμœΌλ―€λ‘œ, Boolean(String) μƒμ„±μž λŒ€μ‹  Boolean.valuesOf(String) νŒ©ν„°λ¦¬ λ©”μ„œλ“œλ₯Ό μ‚¬μš©ν•˜λŠ” 것이 μ’‹λ‹€.

static boolean isRomanNumeral(String s){
  return s.matches("^(?=.)M*(C[MD]|D?C{0,3})" + "(X[CL]|L?X{0,3})(I[XV]|V?I{0,3})$");
}

μ •κ·œ ν‘œν˜„μ‹μ„ ν™œμš©ν•΄ μœ νš¨ν•œ 둜마 μˆ«μžμΈμ§€ ν™•μΈν•˜λŠ” λ©”μ„œλ“œμ΄λ‹€. ν•˜μ§€λ§Œ 이 방식은 String.matches λ©”μ„œλ“œλ₯Ό μ‚¬μš©ν•œλ‹€λŠ” 문제점이 μžˆλ‹€.

/**
     * Tells whether or not this string matches the given <a
     * href="../util/regex/Pattern.html#sum">regular expression</a>.
     *
     * <p> An invocation of this method of the form
     * <i>str</i>{@code .matches(}<i>regex</i>{@code )} yields exactly the
     * same result as the expression
     *
     * <blockquote>
     * {@link java.util.regex.Pattern}.{@link java.util.regex.Pattern#matches(String,CharSequence)
     * matches(<i>regex</i>, <i>str</i>)}
     * </blockquote>
     *
     * @param   regex
     *          the regular expression to which this string is to be matched
     *
     * @return  {@code true} if, and only if, this string matches the
     *          given regular expression
     *
     * @throws  PatternSyntaxException
     *          if the regular expression's syntax is invalid
     *
     * @see java.util.regex.Pattern
     *
     * @since 1.4
     * @spec JSR-51
     */
    public boolean matches(String regex) {
        return Pattern.matches(regex, this);
    }

String.matches λ©”μ„œλ“œ λ‚΄λΆ€μ—μ„œ λ§Œλ“œλŠ” μ •κ·œν‘œν˜„μ‹μš© Pattern μΈμŠ€ν„΄μŠ€λŠ” ν•œ 번 μ“°κ³  버렀져 곧 λ°”λ‘œ 가비지 μ»¬λ ‰μ…˜ λŒ€μƒμ΄ λœλ‹€. Pattern 은 μž…λ ₯받은 μ •κ·œν‘œν˜„μ‹μ— ν•΄λ‹Ήν•˜λŠ” μœ ν•œ μƒνƒœ λ¨Έμ‹ (finite state machine)을 λ§Œλ“€μ–΄ μΈμŠ€ν„΄μŠ€ 생성 λΉ„μš©μ΄ λ†’λ‹€.

finite state machine μ΄λž€

μƒνƒœλ₯Ό 기반으둜 λ™μž‘ν•˜λŠ” κ°œλ…μ˜ λ°©μ‹μœΌλ‘œ, μƒνƒœλ₯Ό 기반으둜 처리되기 λ•Œλ¬Έμ— ν•œ λ²ˆμ— ν•œ 개의 μƒνƒœλ§Œ μ²˜λ¦¬λœλ‹€. μƒνƒœμ— κΈ°λ°˜ν•œ 쑰건에 μ˜ν•΄ μ²˜λ¦¬λ˜λ―€λ‘œ, μƒνƒœ 값이 λ³€κ²½λ˜λ©΄ μƒνƒœμ— λŒ€ν•œ μ’…λ£Œ 및 λ‹€λ₯Έ μƒνƒœλ‘œμ˜ λ³€ν™˜μ„ μ²˜λ¦¬ν•œλ‹€.

https://drehzr.tistory.com/70

μ΄λ ‡κ²Œ 생성 λΉ„μš©μ΄ 많이 λ“œλŠ” 객체가 λ°˜λ³΅ν•΄μ„œ ν•„μš”ν•˜λ‹€λ©΄, μΊμ‹±ν•˜μ—¬ μž¬μ‚¬μš©ν•˜λŠ” 것을 ꢌμž₯ν•œλ‹€.

public class RomanNumerals{

  private static final Pattern ROMAN = Pattern.compile("^(?=.)M*(C[MD]|D?C{0,3})" + "(X[CL]|L?X{0,3})(I[XV]|V?I{0,3})$");

  static boolean isRomanNumeral(String s) {
    return ROMAN.matcher(s).matches();
  }
}

λΆˆλ³€μΈ Pattern μΈμŠ€ν„΄μŠ€λ₯Ό 클래슀 μ΄ˆκΈ°ν™” κ³Όμ •μ—μ„œ 직접 생성해 캐싱해두고, λ‚˜μ€‘μ— isRomanNumeral λ©”μ„œλ“œ ν˜ΈμΆœμ„ 톡해 이 μΈμŠ€ν„΄μŠ€λ₯Ό μž¬μ‚¬μš©ν•˜μ—¬ μ„±λŠ₯을 κ°œμ„ ν•  수 μžˆλ‹€.

ν•˜μ§€λ§Œ, ν΄λž˜μŠ€κ°€ μ΄ˆκΈ°ν™”λœ ν›„ 이 λ©”μ„œλ“œλ₯Ό ν•œ λ²ˆλ„ ν˜ΈμΆœν•˜μ§€ μ•ŠλŠ”λ‹€λ©΄, ROMAN ν•„λ“œλŠ” ν•„μš”μ—†μ΄ μ΄ˆκΈ°ν™” 된 것이닀. lazy initialization(item 83) 으둜 isRomanNumeral λ©”μ„œλ“œκ°€ 처음으둜 호좜될 λ•Œ ν•„λ“œλ₯Ό μ΄ˆκΈ°ν™”ν•˜λ„λ‘ ν•˜μ—¬ λΆˆν•„μš”ν•œ μ΄ˆκΈ°ν™”λ₯Ό 없앨 수 μžˆμ§€λ§Œ, 지연 μ΄ˆκΈ°ν™”λŠ” μ½”λ“œλ₯Ό λ³΅μž‘ν•˜κ²Œ λ§Œλ“œλŠ”λ°, μ„±λŠ₯은 크게 κ°œμ„ λ˜μ§€ μ•Šμ„ λ•Œκ°€ λ§ŽμœΌλ―€λ‘œ κΆŒν•˜μ§€ μ•ŠλŠ”λ‹€.(item 67)

Map μΈν„°νŽ˜μ΄μŠ€μ˜ KeySet λ©”μ„œλ“œλŠ” Map 객체 μ•ˆμ˜ λͺ¨λ“  ν‚€ 값을 담은 Set λ·°λ₯Ό λ°˜ν™˜ν•œλ‹€. KeySet ν˜ΈμΆœμ‹œ μƒˆλ‘œμš΄ Set μΈμŠ€ν„΄μŠ€κ°€ λ§Œλ“€μ–΄μ§„λ‹€κ³  생각할 수 μžˆμ§€λ§Œ, 사싀은 맀번 λ™μΌν•œ Set μΈμŠ€ν„΄μŠ€λ₯Ό λ°˜ν™˜ν•  μˆ˜λ„ μžˆλ‹€. λ°˜ν™˜λœ Set μΈμŠ€ν„΄μŠ€κ°€ 일반적으둜 가변이더라도 λ°˜ν™˜λœ μΈμŠ€ν„΄μŠ€λ“€μ€ κΈ°λŠ₯적으둜 λͺ¨λ‘ λ™μΌν•˜λ©°, λ°˜ν™˜λœ 객체 쀑 ν•˜λ‚˜λ₯Ό μˆ˜μ •ν•˜λ©΄ λͺ¨λ“  객체가 λ™μΌν•œ Map 을 λŒ€λ³€ν•˜κΈ° λ•Œλ¬Έμ— λͺ¨λ“  객체가 λ”°λΌμ„œ 바뀐닀. KeySet λ·° 객체λ₯Ό μ—¬λŸ¬ 개 생성해도 λ˜μ§€λ§Œ, 그럴 ν•„μš”λŠ” μ—†λ‹€.

또 λ‹€λ₯Έ 예둜 auto boxing을 λ“€ 수 μžˆλ‹€. auto boxing은 κΈ°λ³Έ νƒ€μž…κ³Ό λ°•μ‹±λœ κΈ°λ³Έ νƒ€μž…μ„ μ„žμ–΄ μ“Έ λ•Œ μžλ™μœΌλ‘œ μƒν˜Έ λ³€ν™˜ν•΄μ£ΌλŠ” κΈ°μˆ μ΄λ‹€. μ˜€ν†΅ 박싱은 κΈ°λ³Έ νƒ€μž…κ³Ό 그에 λŒ€μ‘ν•˜λŠ” λ°•μ‹±λœ κΈ°λ³Έ νƒ€μž…μ˜ ꡬ뢄을 νλ €μ£Όμ§€λ§Œ, μ™„μ „νžˆ μ—†μ• μ£ΌλŠ” 것은 μ•„λ‹ˆλ‹€.

private static long sum(){
  Long sum = 0L;

  for (long i = 0; i< Integer.MAX_VALUE; i++) {
    sum += i;
  }

  return sum;
}

μœ„ μ½”λ“œλŠ” λͺ¨λ“  μ •μˆ˜μ˜ 총 합을 κ΅¬ν•˜λŠ” λ©”μ„œλ“œλ‘œ, intλ₯Ό μ‚¬μš©ν•˜μ§€ μ•Šκ³  long을 μ‚¬μš©ν•˜κ³  μžˆλ‹€. μ •ν™•ν•œ 닡을 λ‚Ό μˆ˜λŠ” μžˆμ§€λ§Œ, μ œλŒ€λ‘œ κ΅¬ν˜„ν•˜μ˜€μ„ λ•Œλ³΄λ‹€ μ„±λŠ₯μƒμœΌλ‘œ 훨씬 λŠλ €μ§„λ‹€. sum λ³€μˆ˜λ₯Ό long이 μ•„λ‹Œ Long 으둜 μ„ μ–Έν•˜μ—¬ λΆˆν•„μš”ν•œ μΈμŠ€ν„΄μŠ€κ°€ sum += i 연산이 μ΄λ£¨μ–΄μ§ˆ λ•Œλ§ˆλ‹€ μƒμ„±λ˜λŠ” 것이닀. λ‹¨μˆœνžˆ sum의 νƒ€μž…μ„ long으둜만 변경해주어도 μ„±λŠ₯이 κ°œμ„ λœλ‹€. 즉, λ°•μ‹±λœ κΈ°λ³Έ νƒ€μž…λ³΄λ‹€λŠ” κΈ°λ³Έ νƒ€μž…μ„ μ‚¬μš©ν•˜κ³ , μ˜λ„μΉ˜ μ•Šμ€ μ˜€ν† λ°•μ‹±μ΄ μˆ¨μ–΄λ“€μ§€ μ•Šλ„λ‘ μ£Όμ˜ν•΄μ•Ό ν•œλ‹€.

μ‹€μ œλ‘œ μƒν’ˆμ˜ 가격을 계산할 λ•Œ μ˜λ„μΉ˜ μ•Šμ€ auto boxing이 ν”νžˆ λ°œμƒν•œλ‹€. 가격 ν•„λ“œμ— λŒ€ν•œ νƒ€μž…μœΌλ‘œ BigDecimal 을 주둜 μ‚¬μš©ν•˜λŠ”λ° BigDecimal λ‚΄λΆ€ λ©”μ†Œλ“œλŠ” κΈ°λ³Ένƒ€μž…μ„ μ‚¬μš©ν•˜κ³  μžˆλ‹€.

public BigDecimal(long val) {
    this.intCompact = val;
    this.intVal = (val == INFLATED) ? INFLATED_BIGINT : null;
    this.scale = 0;
}
public long longValue(){
    return (intCompact != INFLATED && scale == 0) ? intCompact : toBigInteger().longValue();
}

μ΄λ•Œ BigDecimal.longValue()둜 연산을 ν•˜κ³  μ‹Άλ‹€λ©΄, κΈ°λ³Έ νƒ€μž…μ„ μ‚¬μš©ν•΄μ•Όν•˜λ©°, 그렇지 μ•Šλ‹€λ©΄ λΆˆν•„μš”ν•œ μΈμŠ€ν„΄μŠ€κ°€ 생성될 것이닀.

ν”„λ‘œκ·Έλž¨μ˜ λͺ…ν™•μ„±, κ°„κ²°μ„±, κΈ°λŠ₯을 μœ„ν•΄ 객체λ₯Ό μΆ”κ°€λ‘œ μƒμ„±ν•˜λŠ” 것이라면 일반적으둜 쒋은 일이닀. λΆˆν•„μš”ν•œ 객체λ₯Ό μƒμ„±ν•˜λŠ” 것을 ν”Όν•˜κ³ μž 객체 ν’€(pool)을 μƒμ„±ν•˜λŠ” 것은 ꢌμž₯ν•˜μ§€ μ•ŠλŠ”λ‹€. 자체 객체 풀은 μ½”λ“œλ₯Ό ν—·κ°ˆλ¦¬κ²Œ λ§Œλ“€λ©°, λ©”λͺ¨λ¦¬ μ‚¬μš©λŸ‰μ„ 늘리고 μ„±λŠ₯을 λ–¨μ–΄λœ¨λ¦°λ‹€. JVM의 가비지 μ»¬λ ‰ν„°λŠ” μƒλ‹Ήνžˆ μ΅œμ ν™”κ°€ μž˜λ˜μ–΄μžˆμ–΄ 직접 λ§Œλ“  객체 풀보닀 훨씬 λΉ λ₯Έ κ²½μš°κ°€ λ§Žλ‹€. 방어적 볡사가 ν•„μš”ν•œ μƒν™©μ—μ„œ 객체λ₯Ό μž¬μ‚¬μš©ν–ˆμ„ λ•Œμ˜ ν”Όν•΄κ°€ ν•„μš” μ—†λŠ” 객체λ₯Ό 반볡 μƒμ„±ν–ˆμ„ λ•Œμ˜ 피해보닀 훨씬 큰 것을 μœ μ˜ν•΄μ•Όν•œλ‹€.

Last updated

Was this helpful?