9-2. 유용한 클래스

Hyun Jun·2022년 1월 25일
0

자바의 정석

목록 보기
36/52
post-thumbnail
post-custom-banner

유용한 클래스

java.util 패키지에 포함된 클래스들 중 유용한 것 위주

 

java.util.Objects 클래스

Object클래스의 보조 클래스로, 객체의 비교나 null-check에 사용.

모든 메서드가 static

 

1. isNull

  • 매개 변수: Object obj

  • 반환 타입: boolean

객체가 null이면 true, 아니면 false 반환

 

2. nonNull

  • 매개 변수: Object obj

  • 반환 타입: boolean

isNull과 정반대

 

3. requireNonNull

static <T> T requireNonNull(T obj) {
    if (obj == null) {
        throw new NullPointerException();
    }
    return obj;
}

객체가 null이 아니어야하는 경우에 사용. null이면 NullPointerException을 던짐.

static <T> T requireNonNull(T obj, String msg) { ... }
static <T> T requireNonNull(T obj, Supplier<String> msgSupplier) { ... }

두번째 매개변수로 String을 받아 예외메시지를 설정할 수 있게끔 오버로드 되어있음

명시적으로 참조 객체가 null이면 안된다는 것을 표현할 수 있고, 바로 예외를 던지므로 장애 파악을 빠르게 할 수 있음.

class A {
    String name;
}

class B {
    A a;

    B(A a) {
        this.a = Objects.requireNonNull(a);
    }
}

장애 파악을 빠르게 할 수 있다는 건 아래 예시로 확인 가능

A a = null;
B b = new B(a); // 인스턴스를 생성하려는 시점에 바로 예외 발생

만약 requireNonNull을 사용하지 않았다면

class C {
    A a;

    C(A a) {
        this.a = a;
    }

    getA() {
        ...
    }
}
A a = null;
C c = new C(a); // 인스턴스 생성 시점에 예외 발생이 안됨
c.getA(); // 인스턴스를 활용할 때 예외 발생 (뒤늦은 타이밍)

 

4. compare

  • 매개 변수: Object a, Object b, Comparator c

  • 반환 타입: int

두 객체를 대소 비교하여 같으면 0, 크면 양수, 작으면 음수 반환.

 

5. equals

  • 매개 변수: Object a, Object b

  • 반환 타입: boolean

내부적으로 null checking을 한 후에 Object클래스의 equals 호출.

메서드를 사용할 때 따로 비교 대상 객체들의 null checking을 안해줘도 된다는 장점이 있음.

if (a != null a.equals(b)) {
    ...
}
// 대신에
if (Objects.equals(a, b)) {
    ...
}

6. deepEquals

  • 매개 변수: Object a, Object b

  • 반환 타입: boolean

객체를 재귀적으로 비교하여 다차원 배열의 비교에 유용함.

다차원 배열 간 비교에서 equals에 반복문을 함께 사용하는 대신 Objects.deepEquals로 한방에 가능.

 

7. toString

  • 매개 변수: Object obj

  • 반환 타입: String

내부적으로 null checking을 한 후에 Object클래스의 toString 호출.

두번째 인자로 문자열을 넘기면 객체가 null인 경우 대신 사용할 수 있음.

 

8. hashCode

  • 매개 변수: Object obj

  • 반환 타입: int

내부적으로 null checking을 한 후에 Object클래스의 hashCode 호출.

 

java.util.Random 클래스

Random 클래스의 생성자와 메서드

Math.random()과 다르게 종자값(seed)을 설정할 수 있는데, 이는 난수 생성 로직에 영향을 주는 값.

호출되는 메서드의 종자값이 같으면 동일한 난수를 반환함.

종자값은 생성자에 인자로 넘겨서 지정할 수 있고, 이후에는 setSeed 메서드로 바꿀 수 있음

Random r = new Random(); // 기본 생성자는 현재 시간 관련 값을 종자값으로 가짐
Random r = new Random(1); // 1을 종자값으로 하는 Random 인스턴스 생성

 

nextBytes, nextInt, nextLong, nextFloat, nextDoubleMath.random()처럼 난수를 생성해줌.

아래 두 문장은 동일함

double random1 = Math.random();
double random2 = new Random().nextDouble();

nextInt의 경우는 인자로 정수 n을 넘기면 0 ~ n 사이의 난수를 얻을 수 있음

int randomInt = new Random().nextInt(7); // 0 ~ 7 사이의 정수 반환

📌 [참고] Math.random() 으로 min ~ max 범위의 난수 생성하기

Math.random() * (max - min + 1) + min

식 전체를 묶어 int형으로 형변환시켜주면 정수만 뽑힘

 

활용 예시) 0~9 사이의 난수를 100번 뽑아서 그래프로 나타내기

class RandomExample {
    public static void main(String[] args) {
        Random r = new Random(); // 새 random 인스턴스 생성
        int[] randomNums = new int[100];
        int[] counter = new int[10];

        for (int i = 0; i < randomNums.length; i++) {
            randomNums[i] = r.nextInt(10); // 0 ~ 9 범위의 정수 난수 생성하여 원소로 넣음
        }

        for (int i = 0; i < randomNums.length; i++) {
            counter[randomNums[i]]++; // 100개의 난수의 누적 숫자를 셈
        }

        for (int i = 0; i < counter.length; i++) {
            System.out.println(i + ": " + getGraph('#', counter[i]) + " " + counter[i] + "개"); // getGraph로 각 숫자마다의 누적 개수만큼 그래프를 그림
        }

    }

    public static String getGraph(char c, int length) {
        char[] bar = new char[length];

        for (int i = 0; i < length; i++) {
                bar[i] = c;
        }

        return new String(bar);
    }
}

실행 결과)

0: ###### 6개
1: ############ 12개
2: ############## 14개
3: ####### 7개
4: ########## 10개
5: ######## 8개
6: ######### 9개
7: ############ 12개
8: ############ 12개
9: ########## 10개

 

java.util.regex 패키지 (정규식)

정규식: 텍스트 데이터 안에서 원하는 패턴과 일치하는 문자열을 찾아내는 용도

  • Pattern: 정규식 패턴을 정의하는 역할

  • Matcher: 정의된 패턴을 텍스트 데이터와 비교하는 역할

 

진행 단계

  1. Pattern 클래스의 static메서드인 compile을 호출해 Pattern 인스턴스를 생성

    Pattern p = Pattern.compile(패턴);
  2. Pattern 클래스의 matcher 메서드를 호출해 Matcher 인스턴스를 생성

    Matcher m = p.matcher(비교할 대상);
  3. Matcher 인스턴스에 matches 메서드를 호출해 정규식 패턴에 부합하는지 검사 (boolean값 반환)

    if (m.matches()) {
        ...
    }

    조건식에 자주 쓰이므로 예시를 if문 형식으로 넣었음

 

정규식 패턴들은 교재 p.507 참고

class RegexExample {
    public static void main(String[] args) {
        String[] data = {"apple", "banana", "orange", "pineapple", "kiwi", "pear", "grape"};
        Vector<String> matchResult = new Vector<String>();

        Pattern p = Pattern.compile("p[a-z]*"); // "p로 시작하는 모든 영단어"를 의미하는 패턴
        for (String fruit : data) {
            Matcher m = p.matcher(fruit);
            if (m.matches()) {
                    matchResult.add(fruit); // 패턴에 부합하는 원소들을 matchResult 배열에 추가
            }
        }
        System.out.println(matchResult); // [pineapple, pear]
    }
}

정규식을 괄호로 묶어 grouping을 할 수 있음.

그리고 나서 Matcher의 메서드인 group()을 통해 매칭된 문자열을 group 단위로 쪼개서 발췌할 수 있음.

group()group(0)은 매칭된 문자열 전체를 반환하고, group(n)은 n번째 그룹을 반환함.

class RegexExample2 {
    public static void main(String[] args) {
        String source = "172-008-295-346";
        String pattern = "(0\\d{1,2})-(2\\d{1,2})"; // 0으로 시작하는 2~3자리 숫자, 2로 시작하는 2~3자리 숫자

        Pattern p = Pattern.compile(pattern);
        Matcher m = p.matcher(source);

        while(m.find()) {
            System.out.println(m.group(0)); // (매칭된 문자열 전체) 008-295
            System.out.println(m.group(1)); // (매칭된 문자열 중 첫번째 그룹) 008
            System.out.println(m.group(2)); // (매칭된 문자열 중 두번째 그룹) 295
            System.out.println(m.start() + " ~ " + m.end()); // 3 ~ 9
        }
    }
}

Matcher의 메서드인 find()는 패턴과 일치하는 부분을 찾아내면 true, 못 찾으면 false를 반환함.

find()를 반복적으로 호출하면 이전에 매칭된 부분의 다음부터 매칭을 시작함

start()이나 end()로 매칭되는 문자열의 시작 인덱스 또는 (마지막 인덱스 + 1)을 얻을 수 있음

 

java.util.Scanner 클래스

Scanner는 입력소스(화면, 파일, 문자열)로부터 문자 데이터를 읽어오는 것을 도와주는 역할 (JDK 1.5 부터)

Scanner s = new Scanner(System.in);
String input = s.nextLine();

위 예시는 입력받는 값이 String 타입인 케이스지만, 숫자인 경우엔 nextInt(), nextFloat()과 같은 메서드를 사용함

이외에도 java.io 패키지의 Console 클래스가 있는데, 화면 입출력만을 전문적으로 담당. (JDK 1.6 부터)

Console c = System.console();
String input = c.readLine();

파일로부터 데이터를 읽어오는 경우에는

Scanner s = new Scanner(new File("파일명"));

이렇게 Scanner 생성자에 새 파일 인스턴스를 넘겨주고, 파일이 같은 디렉토리에 있는게 아니라면 파일명 부분에 경로까지 포함해서 입력.

구분자 단위로 끊어서 데이터를 읽을 때는 useDelimiter() 메서드를 사용

Scanner s = new Scanner("123,456,789").useDelimiter(",");
int data1 = s.nextInt();
int data2 = s.nextInt();
int data3 = s.nextInt();

System.out.println(data1); // 123
System.out.println(data2); // 456
System.out.println(data3); // 789

 

java.util.StringTokenizer 클래스

긴 문자열을 지정된 구분자(delimiter)를 기준으로 끊어서 token이라 부르는 문자열 토막을 만드는 역할

Stringsplit() 메서드나 ScanneruseDelimiter() 메서드와 효과는 같지만, 이 둘은 정규식을 받아서 쓰기 때문에 정규식 없이 단일문자로된 구분자를 사용할 때는 StringTokenizer를 사용할 수 있음.

StringTokenizer의 생성자와 메서드

생성자의 매개변수(필수)로 String str, String delimiter 을 받아오고,

3번째 매개변수(선택)로 boolean 값을 넣을 수 있음 (true면 구분자도 하나의 토큰으로 간주)

 

  1. int countTokens()

    전체 토큰의 수를 반환함

  2. boolean hasMoreTokens()

    토큰이 더 남아있는지 알려줌

  3. String nextToken()

    다음 토큰을 반환함

 

예시)

String source = "1111,2222,3333,4444";
StringTokenizer st = new StringTokenizer(source, ",");

while (st.hasMoreTokens()) {
    System.out.println(st.nextToken());
}

실행 결과)

1111
2222
3333
4444

 

java.math.BigInteger 클래스

정수형 타입 중 가장 큰 범위를 가진 long 타입은 10진수로 19자리의 수를 표현할 수 있는데, 이보다 더 큰 값을 다뤄야한다면 BigInteger를 사용함

BigInteger는 내부적으로 int 배열을 사용하여 값을 다룸.

final int signnum; // 부호를 나타냄. 양수는 1, 음수는 -1
final int[] magnitude; // 값

내부적으로 값을 상수로 저장하므로, String처럼 한번 값을 정하면 불변임.

 

BigInteger의 생성

일반적으로 문자열로 표현함 (정수형 리터럴은 표현할 수 있는 값의 한계가 있어서)

BigInteger b1 = new BigInteger("2345073244857230480"); // 10진수 문자열로 생성
BigInteger b2 = new BigInteger("FFFFFF", 16); // 16진수 문자열로 생성
BigInteger b3 = BigInteger.valueOf(81949281447L); // valueOf를 통해 정수형 리터럴로 생성

 

다른 타입으로의 변환

  1. 문자열, byte배열로 변환

    • String toString()

      문자열로 변환

    • String toString(int radix)

      지정된 진법(radix)의 문자열로 변환

    • byte toByteArray()

      byte 배열로 변환

 

  1. 기본형 정수, 실수로 변환

    • int intValue()

    • long longValue()

    • float floatValue()

    • double doubleValue()

 

BigInteger의 연산

BigInteger의 값은 불변이므로, 연산 메서드를 통해 반환되는 BigInteger는 연산 결과값을 담은 새로운 인스턴스임.

  • BigInteger add(BigInteger value)

  • BigInteger subtract(BigInteger value)

  • BigInteger multiply(BigInteger value)

  • BigInteger divide(BigInteger value)

  • BigInteger remainder(BigInteger value)

    value로 나눈 나머지 반환

 

비트 연산 메서드

큰 숫자를 다루다보니 성능 향상을 위해 비트 단위 연산을 수행하는 메서드를 가지고 있음.

and, or, xor, not과 같은 비트 연산자와 더불어

  • int bitCount()

    2진수로 표현했을 때, 1의 개수 반환

  • int bitLength()

    2진수로 표현했을 때, 값을 표현하는데에 필요한 bit 수 반환

  • boolean testBit(int n)

    우측에서 (n + 1)번째 비트가 1이면 true, 0이면 false

  • BigInteger setBit(int n)

    우측에서 (n + 1)번째 비트를 1로 변경

  • BigInteger clearBit(int n)

    우측에서 (n + 1)번째 비트를 0으로 변경

  • BigInteger flipBit(int n)

    우측에서 (n + 1)번째 비트를 반대로 바꿈 (1은 0으로, 0은 1로)

 

java.math.BigDecimal 클래스

double 타입의 범위를 초과하는 실수형을 다룸.

10진 실수를 2진 실수로 정확히 변환할 수 없는데서 오는 오차를 극복하기 위해, BigDecimal은 실수를 애초에 2진 정수로 변환하여 다룸.

 

정수 * 10-scale

  • 정수는 BigInteger를 사용하여 저장

  • scale은 0 ~ Integer.MAX_VALUE 사이의 값을 가질 수 있음

 

private final BigInteger intVal; // 정수
private final int scale; // 지수
private transient int precision; // 정밀도 (정수의 자리수)

BigDecimal의 생성

BigInteger와 마찬가지의 이유로, 문자열로 표현함

BigDecimal b1 = new BigDecimal("123.4567890"); // 문자열로 생성
BigDecimal b2 = new BigDecimal(123.456); // double형 리터럴로 생성
BigDecimal b3 = new BigDecimal(123); // int형 리터럴로 생성 (long도 가능)
BigDecimal b4 = BigDecimal.valueOf(123.456); // valueOf(double)로 생성
BigDecimal b5 = BigDecimal.valueOf(123.456); // valueOf(int)로 생성 (long도 가능)

📌 [주의] double형 리터럴로 생성하면, 오차가 발생할 수 있음

 

다른 타입으로의 변환

  1. 문자열로 변환
  • String toPlainString()

    기호 없이 무조건 숫자로만 표현

  • String toString()

    필요에 따라 지수형태로 표현할 수도 있음

 

BigDecimal value = new BigDecimal("1.0e-10");
System.out.println(value.toPlainString()); // 0.00000000010
System.out.println(value.toString()); // 1.0E-10

 

  1. 기본형 정수, 실수로 변환

    • int intValue()

    • long longValue()

    • float floatValue()

    • double doubleValue()

 

BigDecimal의 연산

BigDecimal의 값은 불변이므로, 연산 메서드를 통해 반환되는 BigDecimal는 연산 결과값을 담은 새로운 인스턴스임.

  • BigDecimal add(BigDecimal value)

  • BigDecimal subtract(BigDecimal value)

  • BigDecimal multiply(BigDecimal value)

  • BigDecimal divide(BigDecimal value)

  • BigDecimal remainder(BigDecimal value)

    value로 나눈 나머지 반환

 

반올림 모드: divide()와 setScale()

다른 연산과는 달리 나눗셈에 대해서는 여러 메서드가 오버로드 되어있음.

  • BigDecimal divide(BigDecimal value)

  • BigDecimal divide(BigDecimal value, int roundingMode)

  • BigDecimal divide(BigDecimal value, RoundingMode roundingMode)

  • BigDecimal divide(BigDecimal value, int scale, int roundingMode)

  • BigDecimal divide(BigDecimal value, int scale, RoundingMode roundingMode)

  • BigDecimal divide(BigDecimal value, MathContext mc)

 

roundingModeBigDecimal에 정의된 ROUND_로 시작하는 이름을 가진 멤버 변수(상수)들 중 택 1

RoundingMode는 연산 결과 처리 방식을 정의한 enum(아래 표 참고)내의 상수들 중 택 1

enum 값 사용이 권장됨

상수의미
CEILING올림
FLOOR내림
UP양수면 올림. 음수면 내림
DOWN양수면 내림. 음수면 올림
HALF_UP반올림 (5 이상부터 올림)
HALF_EVEN반올림 (반올림 자리값이 짝수면 HALF_DOWN, 홀수면 HALF_UP)
HALF_DOWN반올림 (6 이상부터 올림)
UNNECESSARY나누어 떨어지므로 반올림 모드 지정이 필요 없다는 의미

📌 [주의] 나누어떨어지지 않는 경우(ex. 무한소수)는 반올림모드 지정이 없을 시 ArithmeticException을 던짐

 

java.math.MathContext

정밀도와 반올림모드를 하나로 묶어놓은 것

MathContext mc = new MathContext(int precision, RoundingMode roundingMode);

📌 [주의]

  • divide()에서의 scale = 소수점 이하의 자리수

  • MathContext에서의 정밀도(precision) = 정수와 소수점 이하 모두 포함한 자리수

 

scale의 변경

BigDecimal 값은 10n으로 곱하거나 나누는 대신, scale 값을 변경하면 됨

setScale()메서드 사용

  • BigDecimal setScale(int newScale)

  • BigDecimal setScale(int newScale, int roundingMode)

  • BigDecimal setScale(int newScale, RoundingMode roundingMode)

profile
Back-end Engineer 👨‍💻
post-custom-banner

0개의 댓글