반응형

 

익명 클래스는 말 그대로 클래스의 이름이 없다.

익명 클래스는 설계도가 1회성이라는 말이다.

 

 

이름이 없으므로 생성자를 구현할 수 없다.

익명 클래스를 정의하려면 상속을 받거나 인터페이스로 구현해야한다.

 

 

참조 변수에 넣어두고 재사용할 수 있다.

상위 클래스의 생성자 지정, 오버 라이딩은 가능하다.

 

 

 


상속을 받은 익명 클래스

 

상속받아서 사용할 Outer 클래스

 

Outer 클래스를 익명 클래스로 활용

 

참조변수 o는 Outer를 상속받아서 1회성 구현부를 작성하고 인스턴스를 생성한 것과 같다.

 


인터페이스로 구현한 익명 클래스

 

 

추상메서드 2개를 갖는 인터페이스를 정의한 후

 

 

상속과 마찬가지로 인터페이스를 익명 클래스로 구현해서 사용할 수 있다.

 

참조변수 ti는 TestInterface의 추상메서드들을 구현하고 인스턴스를 생성한 것과 같다.

 

 


람다식(함수형 인터페이스)

 

위에서 본 익명클래스들의 생성을 코드 구현을 더 짧게 하고자 하는 것이 람다식이다.

 

마찬가지로 람다식은 단순히 함수를 구현하는 것 같지만 사실 객체의 생성이다.

인터페이스를 구현받아서 추상메서드를 작성하고 익명 객체를 생성한 것이다.

 

 

익명 클래스의 구현을 람다식으로 바꿔줄 수 있다.

(매개변수) -> 메서드 구현부

 

대신, 조건으로 인터페이스는 하나의 추상 메서드만을 갖고 있어야 한다.

하나의 추상 메서드만을 갖고 있는 인터페이스를 함수형 인터페이스라 한다.

 

람다식은 함수형 인터페이스를 이용해서 익명 객체로 구현하는 과정을 짧게 만든 것이다.

함수형 인터페이스가 갖고 있는 하나의 추상메서드의 1회성의 구현부를 만든 후 객체를 생성한 것

 

 


자바에는 이러한 함수형 인터페이스를 미리 만들어 둔 API가 있다.

https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/util/function/package-summary.html

 

java.util.function (Java SE 15 & JDK 15)

Functional interfaces provide target types for lambda expressions and method references. Each functional interface has a single abstract method, called the functional method for that functional interface, to which the lambda expression's parameter and retu

docs.oracle.com

 

 

 

 


 

반응형
반응형

[JAVA] String 참조값 비교와 StringBuilder, StringBuffer 사용 이유


 

String에서 + 연산을 사용하면 안 되는 이유

StringBuilder, StringBuffer를 사용하는 이유

 


String에서 + 연산을 사용하면 메모리를 추가로 사용한다.

 

 

str1, str2 는 같은 곳을 참조함 (String pool에서의 같은 위치)

 

str3는 String을 + 연산을 사용해서 "abc"를 만들었는데도 String pool의 "abc"를 참조하지 않는다.

+연산을 String에서 하게 되면 String pool에서 찾지 않고 마치 new("abc")를 한 것처럼 "abc" 새로운 메모리가 생성된다.

이는 GC의 부담, 메모리의 낭비를 야기한다.

그러므로 String에서는 + 연산을 지양해야 함

 

== 연산자가 아닌 equals()를 사용하면 문자열은 전부 같으므로 결과는 모두 true이다.

 


StringBuilder, StringBuffer

 

 

StringBuilder도 내부적으로 toString()을 처리할 때 new 연산자를 사용하므로 새로운 메모리가 생성되지만,

toString()할 때 new 연산자를 단 한번 사용하기 때문에 여러 문자 결합에서 이점을 갖는다.

 


 

 

반응형
반응형

[JAVA] 자바 접근 제어자


자바의 접근제어자는 4종류가 있다.

 

                                                         public : 제한 없음

                                                         protected : 패키지 내부  //  다른 패키지여도 상속(extends) 했다면 사용 가능

                                                         defalut : 패키지 내부

                                                         private : 클래스 내부

 

 

이 중 접근제어자로 protected가 헷갈리는 경우가 많아 정리하고자 한다.

 

protected는 같은 패키지 내부에서 사용 가능하다. 이점은 헷갈리지 않는다.

헷갈리게 하는 부분은 다른 패키지여도 상속했다면 사용가능하다는 의미에서 헷갈린다.

 

결론부터 말하자면 protected는 메서드, 멤버변수에 사용가능하고

다른 패키지에서 사용하려면 protected 메서드, 멤버변수를 가진 class가 public이어야 한다.

 


테스트 구조도

 

패키지 Classtest

  • (default) DefaultClass 
  • (public) PublicClass

 

패키지 Classtest2

  • MainCalss

 

 

다른 패키지에서 DefaultClass를 import할 수 없다.

default는 같은 패키지에서만 사용할 수 있기 때문이다.

 


이제 PublicClass를 이용해서 protected를 테스트해보자.

PublicClass의 내부는 다음과 같다.

public 변수 number1

protected 변수 number 2

 

 

다른 패키지인 MainClass에서 PublicClass를 import 후 사용을 해봤다.

protected인 number 2가 보이지 않는다.

protected는 상속해야 사용 가능하기 때문이다.

 

 

상속을 하고 나서야 number 2를 확인할 수 있다.

 

 


사용 가능한 접근제어자

 

Class : public, default

멤버, 메서드, 생성자 : public, protected, default, private

 

 

Class에서는 public default 밖에 사용하지 못함에 주의하자.

 


 

반응형
반응형

[JAVA] 자바 String, StringBuilder 클래스 활용


 

자바를 사용하다 보면 문자열을 처리해줘야 할 경우가 많이 생긴다.

이때 String과 StringBuilder 클래스를 활용하면 좋다. 그리고 경우에 따라 String을 써야 할 경우와 StringBuilder를 써야 할 경우가 구분된다.

어떤 경우에 String과 StringBuilder를 맞게 써야 하는지 정리하고, 함수들의 활용법을 정리하고자 한다.

 


String과 StringBuilder의 구분

 

String 클래스와 StringBuilder는 용도가 다르다.

 

String은 클래스 명 그대로 문자를 이용해서 활용을 해야 할 때 사용하는 클래스이다.

* 문자를 변경, 검색, 반복, 대소문자 변환, 비교, 분리 등

"문자 특성"을 이용하기 위해 사용하는 클래스이다.

 

StringBuilder문자열을 배열처럼 관리하며 추가, 삭제, 삽입을 용이하게 하기 위해 사용한다.

* 문자를 추가, 삭제, 삽입, 뒤집기 등

"배열 index"를 이용하여 문자를 관리하는 클래스이다.

 

문자 특성을 이용한 작업을 처리할 땐 String 클래스를 이용하고, 변경이 빈번한 문자들을 배열처럼 관리하고 싶을 때 StringBuilder를 이용한다.

 

* String 클래스는 함수를 사용하면 반환 값을 사용해야 하므로 대입 연산자를 이용해야 한다.

ex) str = str.trim();

* StringBuilder는 내부적으로 추가, 삭제, 삽입, 변경을 처리하기 때문에 반환 값을 사용할 필요가 없다.

ex) sb.append(var);

 


자주 사용하는 String과 StringBuilder의 공통 함수

 

  • length() : 문자열의 총길이를 반환한다.
  • charAt(index) : index 위치에 해당하는 문자를 반환한다.
  • indexOf(String) : 문자열이 위치하는 index를 반환한다. (String에서는 char도 가능하다.)
  • substring(from, to) : from부터 to까지 해당되는 문자열을 반환한다. (to index는 포함하지 않는다.)

 


자주 사용하는 String 클래스 함수

 

비교

  • compareTo(String) : String과 비교 후 String보다 문자가 작다면 1, 크다면 -1, 같다면 0을 반환한다.
  • compareToIgnoreCase(String) : 대소문자 차이를 무시하고 비교한다.
  • equals(Object) : Object와 비교 후 같다면 true, 다르면 false를 반환한다. String 클래스는 문자열을 비교한다.

 

검색

 

  • contains(CharSequence) : CharSequence가 포함되어있는지 확인한 후 포함하면 true, 포함하지 않으면 false를 반환한다.
  • startWith(String) : String으로 시작하면 true, 아니면 false를 반환한다.
  • endWith(String) : String으로 끝나면 true, 아니면 false를 반환한다.

 

 

변환

 

  • concat(String) : String과 합친 후 합친 문자열을 반환한다.
  • repeat(int count) : count만큼 반복 후 반환한다.
  • replace(char or CharSequence, char or CharSequence) : char, CahrSequence 둘 다 가능하며 앞 인자를 뒷 인자로 변경한다.
  • toLowerCase() : 소문자로 변환 후 반환한다.
  • toUpperCase() : 대문자로 변환 후 반환한다.
  • trim() : 문자열의 양쪽 공백을 제거 후 반환한다.
  • toCharArray() : char 배열 자료형으로 반환한다.

 

 

분리, 결합

  • split(String regex) : regex를 기준으로 자른 후 String []로 반환한다.
  • String.join("str" ,String[]) : str을 사이에 넣어주며 String [] 배열을 합친다.

 

* CharSequence => String, StringBuilder, StringBuffer 등

 


자주 사용하는 StringBuilder 클래스 함수

 

추가

  • append() : StringBuilder에 추가한다.

 

삭제

  • delete(from, to) : from부터 to까지 해당되는 index들을 제거한다. (to는 제외)
  • deleteCharAt(index) : index에 해당하는 문자 하나를 제거한다.

 

삽입

  • insert(index, Object) : index위치에 Object를 추가한다.

 

변경

  • replace(from, to, String) : from, to 부분을 String으로 변경한다. (to는 제외)
  • setLength(len) : len만큼 길이를 변경한다. (len을 0으로 설정하면 내부가 비워지는 효과를 볼 수 있다.)
  • setChar(index, char) : 해당 index를 char로 변경한다.
  • reverse() : 문자열을 뒤집는다.

 

주의: StringBuilder는 equals()가 주소 비교를 한다.  toString()을 해야 한다.


자바 문서

https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/lang/String.html#concat(java.lang.String) 

 

String (Java SE 15 & JDK 15)

All Implemented Interfaces: Serializable, CharSequence, Comparable , Constable, ConstantDesc public final class String extends Object implements Serializable, Comparable , CharSequence, Constable, ConstantDesc The String class represents character strings.

docs.oracle.com

https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/lang/StringBuilder.html

 

StringBuilder (Java SE 15 & JDK 15)

All Implemented Interfaces: Serializable, Appendable, CharSequence, Comparable public final class StringBuilder extends Object implements Serializable, Comparable , CharSequence A mutable sequence of characters. This class provides an API compatible with S

docs.oracle.com

 

반응형
반응형

[JAVA] 자바 오버 로딩 (Overloading)


하나의 클래스 안에서 여러 개의 같은 이름의 메서드를 여러 개 정의하는 것을 오버 로딩이라고 합니다.

이름이 같은 함수가 여러 개의 인자를 갖고 있는 것처럼 보여서 과적되었다고 표현하는 것입니다.

오버 로딩은 조건이 필요한데, 3가지를 기억하시면 됩니다.

  • 메서드의 이름이 같아야 한다.
  • 매개변수의 개수 또는 타입이 달라야 한다.
  • 반환 타입이 다른 것은 오버 로딩에 영향을 미치지 않는다.

 

결국 오버 로딩이란 메서드들의 이름은 같지만, 매개변수의 개수 또는 타입이 달라질 때 사용하는 것입니다.

주의해야 할 점은 반환 타입이 다르다고 할지라도 이름과 매개변수가 같으면 같은 함수로 인식된다는 점입니다.

반환 타입은 오버 로딩에 영향을 주지 않습니다.

반환 타입은 넘겨주는 쪽에서 제대로 된 타입만 넘겨준다면, 받는 쪽에서 형 변환을 통해 사용할 수 있기 때문입니다.

 


실습 코드

 

public class OverloadingTest {
    public static void main(String[] args) {
        add(4,5);
        add(3L,2L);
        add(1,2,3);
    }
    static void add(int a, int b){
        System.out.println(a + b);
    }
    
    static void add(long a, long b){ // 타입이 다른 경우
        System.out.println(a + b);
    }
    
    static void add(int a, int b, int c){ // 개수가 다른 경우
        System.out.println(a+b+c);
    }
}
 
cs

 

 

매개변수의 숫자를 더하는 함수로 add 함수를 만들어놓고,

추가적으로 오버 로딩하여 타입이 다른 경우와 개수가 다른 경우를 테스트해보시면 함수의 이름이 같다고

하더라도 오버 로딩이 적용되어 올바르게 작동하는 점을 보실 수 있습니다.

 


반환 타입만 바꾼 경우

 

 

같은 함수의 이름과 매개변수를 같게 하여 반환 타입만 바꿔본 경우입니다. 같은 함수로 인식되어 오류가 발생합니다.

반환 타입만 바꾼다고 해서 오버 로딩이 되지 않으며 컴파일 오류가 발생합니다.

 


기변 매개변수

 

public class Main1{
    public static void main(String[] args) {
        System.out.println(defaultParameter(1,2));
        System.out.println(defaultParameter(1,2,3,4));
    }
 
    static int defaultParameter(int... a) {
        int sum = 0;
        for (int i : a) {
            sum+=i;
        }
        return sum;
    }
}
cs

 

자바에서 함수의 매개 변수의 개수를 바꿔가며 줄 때 함수의 매개 변수의 선언부에 타입... 변수라고 지정해주면

타입만 맞다면 최대 개수의 255개 내에서 제한없이 함수를 호출할 수 있습니다. 사실 배열 타입으로 선언된 매개변수입니다.

주의할 점은 기본 매개변수와 같이 사용한다면 항상 선언부의 맨 뒤에 와야 합니다.


 

오버 로딩의 개념 자체는 어렵지 않으며, 오버 라이딩과 혼동할 수 있으니 오버 라이딩도 함께 공부하시면 좋습니다.

반응형
반응형

[JAVA] 자바 BufferedReader 사용법


자바에서 입력을 받을 때 Scanner 클래스로 입력을 받아왔었다.

BufferedReader가 사용하기 불편해서 Scanner가 등장한 걸로 알고 있지만 백준 문제를 풀다 보면 Scanner를 사용했을 때

입력 자체에서 시간 초과가 걸리는 경우가 많아서 BufferedReader를 다시 사용하게 되었다.

 

 

Scanner는 내부적으로 정규 표현식이 너무 많이 적용되어있어서 parse 할 때는 편리하지만, 성능이 희생당한다.

 

 

추가로 출력을 해주는  BufferedWriter도 있지만, 출력을 할 때 StringBuilder에 담아서 출력만 해도 출력 시간 초과는 해결된다.

이 글에서는 BufferedReader만 간단하게 사용하는 법을 정리하기로 했다.

 

 


BufferedReader 사용법

 

 

1
2
3
4
5
6
7
8
9
10
11
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
 
public class BR {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        String str = br.readLine();
        System.out.println(str);
    }
}
cs

 

사용하기 위해선 3가지 코드를 작성해주어야 한다.

 

import java.io.BufferedReader;

import java.io.IOException;

import java.io.InputStreamReader;

import를 이런식으로 따로 해주거나

 

 
import java.io.*;
한번에 import를 처리해줘도 된다.

 

 

BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

두개의 보조 스트림 BufferedReader, InputStreamReader를 사용해서 입력 객체를 생성해준다.

 

main함수에 throws IOException를 추가해주면 기본적인 준비는 마쳤다.

 
이제 br.readLine() 메서드로 입력을 받아줄 수 있다.

고려해야할 점은 br.readLine()의 return은 String이므로 String으로 입력을 받아야한다.

또한 BufferedReadr는 Line 단위로 읽는다.

 

자주 사용하는 추가적인 함수들

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import java.util.*;
import java.io.*;
 
public class BR {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        String str = br.readLine();
        System.out.println(str);
 
        // 정수 변환시
        int n = Integer.parseInt(str);
 
        // 문자를 자를 시
        StringTokenizer st = new StringTokenizer(br.readLine());
        String str1 = st.nextToken();
        int m = Integer.parseInt(st.nextToken());
 
        String[] strs = br.readLine().split(" ");
 
    }
}
 
cs

 

 

문자를 입력을 받은 경우 보통 두 가지 경우가 발생한다.

  • 입력받은 문자를 숫자로 변환할 경우 
  • 입력받은 문자를 특정 문자 기준으로 자를 경우

 

11줄) 문자를 숫자로 변환할 경우는 Integer.parseInt()로 변환해주면 된다.

 

문자를 특정문자 기준으로 자를 경우 두 가지 방법이 가능하다.

14줄) StringTokenizer를 사용할 경우 StringTokenizer 생성자에 입력받을 문자를 넣은 후, nextToken() 함수로 하나 씩 가져와 사용할 수 있다.

공백이 아닌 특정 문자 기준으로 자르고 싶을때에는 new StringTokenizer(br.readLine(), "자를문자");

 

18줄) String.split()을 사용할 경우 반환을 String [] 배열로 받아주어야 한다. split() 함수의 인자로 자를 문자를 입력하면 된다.

 

 


 

반응형
반응형

[JAVA] 자바 Iterator와 ListIterator 사용법


자바에서는 Collection 인터페이스를 구현한 자료구조인 ArrayList, LinkedList, HashSet, TreeSet 등의 요소를 하나씩 반복하여

모든 요소를 확인할 수 있는 Iterator (단방향) ListIterator(양방향)가 있습니다.

 


Iterator 구현도

 

인터페이스가 추상메서드를 갖고 있다면 해당 인터페이스를 구현하는 클래스는 모두 해당 메서드의 구현부를 작성해야 합니다.

 

Iterator는 iterator()라는 함수명을 갖고 있는 추상 메서드가 반환을 합니다.

iterator() 추상 메서드의 위치는 Iterable이라는 인터페이스에서 작성을 해뒀습니다.

결국 Iterable을 구현한 모든 클래스는 iterator()라는 함수의 구현부를 작성해야합니다.

 

Collection 인터페이스

Collection 인터페이스가 Iterable를 상속받았습니다.

그래서 Collection을 구현한 자료구조 API는 iterator()함수를 구현을 해놨습니다.

실제 구현부는 ArrayList, LinkedList, HashSet, TreeSet 와 같은 실제 인스턴스를 생성하는 클래스에서 구현을 합니다.

설계 방식은 전부 다르므로 Iterator를 통해 요소를 가져오고 삭제하는 과정도 따로 작업을 해놨습니다.

 

iterator()를 호출하면 Itr이라는 내부 클래스명으로 Iterator를 상속받고, 객체를 생성해서 돌려줍니다.

iterator()의 반환인 Iterator 인터페이스가 Collection 순회를 돕는 hasNext(), next(), remove()를 구현하도록 하고 있습니다.

 

Iterator를 상속했으니, Iterator it = list.iterator() 와 같이 사용합니다.

 

주의점

List는 저장 순서를 유지하기 때문에 iterator를 사용하면 저장 순서를 유지하며 요소를 확인할 수 있지만,

Set은 저장 순서를 유지하지 않기 때문에 저장 순서대로 요소를 확인할 수 없습니다.

 

인터페이스 구현 순서

  1. Iterable
  2. Collection
  3. List, Set
  4. ArrayList, LinkedList, HashSet, TreeSet 등

 

아래 사진은 ArrayList 내부에 Iterator를 반환하는 iterator() 함수의 작성 내용입니다.

 

ArrayList iterator()

iterator()의 직접적인 구현은 ArrayList, LinkedList, HashSet, TreeSet에 작성하는 걸 확인할 수 있습니다.

iterator() 함수를 호출하게 되면 Itr() 인스턴스를 생성하여 return합니다.

Itr은 내부 클래스로 Iterator 인터페이스의 추상 메서드 hasNext(), next(), remove() 를 모두 구현합니다.

Iterator의 메서드는 JAVA API에서 확인할 수 있습니다.

 

https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/util/Iterator.html

 

Iterator (Java SE 15 & JDK 15)

Type Parameters: E - the type of elements returned by this iterator All Known Subinterfaces: EventIterator, ListIterator , PrimitiveIterator , PrimitiveIterator.OfDouble, PrimitiveIterator.OfInt, PrimitiveIterator.OfLong, XMLEventReader All Known Implement

docs.oracle.com

 

 


Iterator, ListIterator 사용법

 

Iterator의 구현도는 복잡하지만 실제 사용하려는 프로그래머 입장에서는 iterator의 사용은 아주 단순합니다.

주의할 점은 Iterator, ListIterator의 cursor 위치는 시작할 때 맨 앞 요소 그 앞에 있습니다.

cursor의 위치는 요소를 가리키고 있는 것이 아니라 요소와 요소 사이에 있다라고 생각하시면 됩니다.

 

 

Iterator의 사용

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import java.util.*;
 
public class IteratorTest {
    public static void main(String[] args) {
        ArrayList<Integer> arr_list = new ArrayList<>();
        arr_list.add(1);
        arr_list.add(2);
        arr_list.add(3);
        arr_list.add(4);
 
        // Iterator
        Iterator<Integer> itr = arr_list.iterator();
 
        while(itr.hasNext()){
            int n = itr.next();
            System.out.print(n + " ");
            if(n == 2)
                itr.remove();
        }
        System.out.println();
        System.out.println(arr_list);
    }
}
cs

 

12줄) iterator() 함수는 Iterator 인터페이스 return으로 받을 수 있는 Iterator를 Type으로 선언하여 사용해주시면 됩니다.

14줄) hasNext() 함수는 다음 요소가 있다면 true, 없다면 false를 return 합니다. 마지막 요소까지 확인할 수 있습니다.

15줄) next() 함수는 cursor 위치를 오른쪽으로 옮긴 후 왼쪽 요소를 반환합니다. cursor 위치는 요소와 요소 사이입니다.

* cursor가 지나온 요소를 반환한다고 생각하시면 됩니다.

18줄) remove() 함수는 next() 함수의 반환 값을 삭제합니다. 실제 ArrayList에도 영향을 미칩니다.

 

결과

 

 

 

ListIterator의 사용

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import java.util.*;
 
public class ListIteratorTest {
    public static void main(String[] args) {
        ArrayList<Integer> arr_list = new ArrayList<>();
        arr_list.add(1);
        arr_list.add(2);
        arr_list.add(3);
        arr_list.add(4);
 
        // ListIterator
        ListIterator<Integer> it = arr_list.listIterator();
        System.out.print(it.next()+ " ");
        System.out.print(it.next()+ " ");
        System.out.print(it.previous()+ " ");
 
        System.out.println();
        System.out.println(arr_list);
 
    }
}
cs

 

ListIterator는 Iterator와 사용방법이 비슷합니다. 대신 ListIterator는 이전 요소로 되돌아갈 수 있습니다.

 

13, 14줄) it.next()를 부르면 다음 요소로 넘어갑니다. 15줄) it.previous() 호출하면 이전 요소로 되돌아갈 수 있습니다.

이런 식으로 양방향으로 이동 가능한 것이 ListIterator의 특징입니다.

 

여기서 cursor의 위치가 요소와 요소 사이에 있다는 것을 출력을 통해 확인해볼 수 있습니다. 

13줄) next() 함수를 호출하면 결과는 1입니다. cursor는 1과 2 사이에 있으며 왼쪽 요소(1)를 반환합니다.

14줄) next() 함수를 한번 더 호출하면 결과가 2입니다. cursor는 2와 3 사이에 있으며 왼쪽 요소(2)를 반환합니다.

 

여기까지 코드를 실행하면 현재 cursor의 위치는 2와 3사이 입니다.

이제 15줄) previous() 함수를 호출하면 cursor가 왼쪽으로 이동하며 cursor의 위치는 1과 2 사이가 됩니다.

대신 반환 요소를 현재 위치에서 오른쪽의 있는 요소(2)를 반환합니다.

이렇듯 커서는 요소와 요소 사이에 있으며, cursor가 이동한 후, 지나온 요소를 반환한다고 생각하시면 됩니다.

 

결과

 

반응형
반응형

[JAVA] 자바 Comparable과 Comparator 사용법


자바에서 ComparableComparator은 둘 다 인터페이스이며, 정렬을 위해서 사용한다.

인터페이스이므로 구현을 통해 사용해야 하며 기본형 비교가 아닌 객체 비교를 위해 만들어졌다.

 

결론부터 말하자면, Comparable객체 내부에 비교 기준을 부여하여 다른 객체와 비교를 하는 것이고,

Comparator객체 외부에서 비교할 두 객체를 비교하여 비교 기준으로써의 역할을 한다.

여기서 주목할 점은 정렬을 해주는 것이 아니라, 비교 기준을 제공해준다는 점이다. 직접적으로 정렬을 진행하는 부분은 유틸 클래스가 처리해준다.

 

배열의 정렬을 직접 구현해보았다면 두 요소를 비교할 때 비교 연산자 크기 비교를 통해 true, false의 결과로 Swap을 하여 정렬을 한다는 점을 알 수 있을 것이다. 객체 비교에서는 Comparable과 Comparator는 바로 이러한 true, false 같은 정렬 기준을 제공하고, 유틸 클래스가 Swap을 처리하는 코드라고 생각하면 된다.

 

일반적으로 자바에서는 기본형 타입의 비교는 비교 연산자(==,<=,>=,<,>)등을 통해 손쉽게 비교할 수 있다. 하지만 객체 내부의 어떤 field를 비교하고 싶을 때에 객체 자체를 기본형 타입처럼 비교 연산자를 사용하게 되면 객체의 참조값을 비교하기 때문에 올바른 비교 결과를 얻을 수 없다. 그러므로 객체 내부 특정 field를 이용하여 정렬하고자 할 때를 위해 ComparableComparator가 필요하게 되었다. field를 비교하게 되면 그 결과로 객체를 정렬을 할 수 있다.

 

 


Comparable과 Comparator를 사용하기 이전에 알아야 할 것

 

예를 들어 배열의 요소가 두 개인 3과 5 인 배열을 오름차순 한다고 가정해보자.

 

배열이 [3, 5] 순서로 정렬돼있다고 가정했을 때

두 값을 뺄셈 연산하면 그 결과가 -2이다. [ ex) 3 - 5= - 2 ] 그러므로 뒤 값이 큰 걸 알 수 있다. 오름차순 정렬이므로 그대로 둔다. 

 

배열이 [5, 3] 순서로 정렬돼있다고 가정했을 때

두 값을 뺄셈 연산하면 그 결과가 2이다. [ ex) 5 - 3 = 2 ] 그러므로  앞 값이 큰 걸 알 수 있다. 오름차순 정렬을 해야 하므로 두 수의 위치를 Swap 한다.

 

오름차순 정렬에서는 뺄셈 결과가 음수면 앞 값이 작으므로 정렬이 올바르다. 그 위치 그대로 둔다.

뺄셈 결과가 양수면 앞 값이 크므로 오름차순 정렬을 위해 Swap을 해줘야 함을 알 수 있다.

밑에서 사용할 compareTo 함수는 return이 양수면 Swap 시킨다.

곧 이 말은, 뺄셈의 결과가 어떤 수이냐에 따라 Swap을 할지 말지인 정렬 기준을 제공할 수 있다는 점이다.

 

 


Comparable 사용법

 

Comparable은 위에서 서술했듯, 인터페이스이므로 추상 메서드를 갖고 있다. 갖고 있는 추상 메서드는 compareTo 하나이다. Comparable의 사용법은 객체 내부에서 비교 기준을 제공하므로 비교할 객체에 Comparable을 구현받아 객체 내부에 Comparable의 메서드인 compareTo를 Overriding 하여 사용해야 한다.

 

사람의 정보(이름, 나이)가 들어있는 객체를 정의하고 나이 순서로 정렬하고자 할 때

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class PersonComparable implements Comparable<PersonComparable> {
    String name;
    int age;
 
    PersonComparable(String name, int age) {
        this.name = name;
        this.age = age;
    }
 
    @Override
    public int compareTo(PersonComparable o) { // compareTo를 overring하여 내부 field인 age를 비교
        return this.age - o.age;
    }
}
cs

compareTo는 객체 내부(자신)와 외부에서 매개변수로 들어오는 다른 객체와의 나이를 뺄셈 하여 정렬 기준을 제공할 수 있다.

 

 


Comparator 사용법

 

Comparator가 갖고 있는 추상 메서드는 compare이며, compareComparable compareTo와는 다르게 매개변수를 두 개를 받는다. 그 이유는 compare는 자신과 다른 것을 비교하기 위한 목적이 아니라 두 객체를 외부에서 인자로 받아 비교하기 위함이다.

 

마찬가지로 사람의 정보(이름, 나이)가 들어있는 객체를 똑같이 정의하고 나이 순서로 정렬하고자 할 때

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class PersonComparator{
    String name;
    int age;
 
    PersonComparator(String name, int age) {
        this.name = name;
        this.age = age;
    }
 
    @Override
    public String toString() {
        return name+", age="+age;
    }
}
 
 
// 외부에서 인터페이스 Comparator 구현한 정렬기준인 Class (compare method)
class SortStandard implements Comparator<PersonComparator>{
    @Override
    public int compare(PersonComparator p1, PersonComparator p2) {
        return (p1.name).compareTo(p2.name);
    }
}
cs

Comparable방식은 구현한 객체에 Comparable을 직접 구현하여 내부에 compareTo 메서드를 정의하는 방식이다. 반면 Comparator은 외부에서 SortStandard라는 객체를 새로 정의하여 Comparator을 구현하였다. 이러한 이유는 Comparator는 외부에서 정렬 기준이 되므로 SortStandard 객체를 정렬 기준으로 사용할 것이기 때문이다.

 

이 부분을 아래 코드를 통해 본다면 이해가 갈 것이다.

 

 


 ComparableComparator의 비교

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
       //---------------------------------Comparable 사용-----------------------------------//
       ArrayList<PersonComparable> cble = new ArrayList<>();
        cble.add(new PersonComparable("A"100));
        cble.add(new PersonComparable("B"1));
        cble.add(new PersonComparable("C"40));
 
        System.out.println("comparable 정렬 전 "+ cble);
        Collections.sort(cble); // 객체 내부에 정렬기준 (Comparable - compareTo)
        System.out.println("comparable 정렬 후 "+ cble);
 
 
        //---------------------------------Comparator 사용------------------------------------//
        ArrayList<PersonComparator> ctor = new ArrayList<>();
        ctor.add(new PersonComparator("다가"100));
        ctor.add(new PersonComparator("가나"30));
        ctor.add(new PersonComparator("나나"50));
 
        System.out.println("comparator 정렬 전 "+ ctor);
        Collections.sort(ctor, new SortStandard()); // 외부에 정렬기준Class (Comparator - compare)
        System.out.println("comparator 정렬 후 "+ ctor);
cs

실질적으로 정렬을 해주는 유틸 Class인 Collentions.sort 메서드를 사용하여 정렬을 해보자.

사용하는 부분(8번, 19번 라인)을 보자.

 

Comparable 부분은 sort함수에 ArrayList만 인자로 넣어 정렬을 시킨다. Collections.sort(cble);

PersonComparable 객체는 내부에 정렬 기준인 compareTo가 있기 때문에 문제없이 정렬을 할 수 있다.

 

반면, Comparator 부분은 사용법이 다르다. Collections.sort(ctor, new SortStandard());

sort 함수의 인자로 ArrayList인 ctor 이외에도 추가로 new SortStandard()를 부여했다.

 

이전 코드에서 PersonComparator의 설계를 보면 비교 기준이 객체 내부에 존재하지 않는다. Collections.sort()로 정렬을 하려면 비교 기준이 있어야 하는데 PersonComparator는 내부에 객체의 비교 기준이 없으므로 sort() 메서드의 추가적인 인자로 정렬 기준인 new SortStandard()을 준 것이다.

 

이 부분이 바로 정렬 기준인 Comparator를 구현한 객체를 사용한 부분이다. ComparatorComparable과 다르게 외부에서 정렬 기준을 설정한다는 의미가 이 코드를 통해 알 수 있다. 

 

또한, ComparatorComparable의 역할은 결국 두 수를 비교하여 Swap을 할지 말지를 결정한다.

 


문자 비교는 어떻게 할까

 

1
2
3
4
5
      // 문자 비교
    @Override
    public int compareTo(PersonComparable o) { // 자기자신 객체와 비교할 다른 객체
        return this.name.compareTo(o.name);
    }
cs

문자열의 비교는 compareTo의 return부분을 String클래스의 compareTo 메서드의 결괏값을 정렬 기준으로 보내주어 객체 비교를 할 수 있다.

즉, compareTo(PersonComparable o)는 객체 비교를 위한 compareTo이고,

this.name.compareTo(o.name)은 String 문자 비교를 위한 compareTo이다.

 

반응형
반응형

(Visual Studio) 한 프로젝트 안에서 소스파일 각각 실행하는 법


 

알고리즘 문제나 책의 연습문제들을 풀다 보면 추가적으로 다른 프로젝트 생성하는 것이 귀찮아서

한 프로젝트 안에서 소스파일을 여러 개 만들어서 빠르게 코딩하고 싶어 집니다. 

 

하지만 위처럼 한 프로젝트 안에서 main 소스파일이 두 개 이상일 때 컴파일되지 않으며 오류 메시지로

"fatal error LNK1169: 여러 번 정의된 기호가 있습니다." 라는 문구가 발생하여 실행이 불가능합니다.

이는 한 프로젝트 안에 main이 두 개여서 발생하는 오류 메시지입니다.

 

한 프로젝트 안에서 main을 갖는 소스파일은 반드시 하나여야 하므로

결과적으로 한 프로젝트안에 main을 갖는 소스파일들을 하나빼고 전부 제외시켜주어야 합니다.

 

프로그램 구동을 위해서 main을 하나만 남겨주는 방법으로 여러 가지가 있지만

이 글에서 소개할 방법인 1. 프로젝트에서 제외하는 방식, 2. 빌드에서 제외하는 방식 두 가지 해결방법을 알려드리겠습니다.

 


 

해결방법 첫 번째

 

그림처럼 소스파일의 오른쪽 마우스를 누르고 프로젝트에서 제외를 하면 해당 소스파일은 프로젝트에서 빠르게 제거해줄 수 있습니다.

이처럼 제거하는 것은 소스파일을 완전히 삭제하는 것이 아니라 컴퓨터의 로컬 저장소에는 남고 visual studio에서만 인식하지 못하게 하는 방식입니다.

이렇게 하면 main이 하나이므로 정상 구동됩니다.

 

제거해놨던 소스파일을 이용하기 위해서 visual studio로 다시 불러들이고 싶을 때는 아래 사진과 같이

 

(소스파일 오른쪽 마우스 클릭 - 추가 - 기존 항목)에서 제거했던 해당 소스파일을 찾아서 visual studio에 다시 복구할 수 있습니다.

로컬저장소에 남기고 프로젝트에서 제외되므로 보기에 깔끔하지만 다시 이용하고 싶을때 다시 불러와야 하기 때문에 조금 번거로울 수 있는 방법입니다. 

 


 

두 번째 방법

소스파일 속성에서 빌드에서 제외시켜 해당 소스파일을 구동되지 못하게 하는 방법입니다.

 

 

소스파일 오른쪽 마우스 클릭 - 속성 - 빌드에서 제외(예)를 설정하시면 컴파일 시 해당 소스파일을 제외한 채로 컴파일되어 방해하지 않게 만들 수 있습니다.

 

빌드에서 제외하게 되면 위 사진처럼 소스파일 이름명 옆에 "-" 표시로 제외된 것을 육안으로 확인 가능합니다.

다시 복구하고 싶을 때는 반대로 빌드에서 제외 - 아니오를 설정 해주시면 복구됩니다.

 

저는 빌드에서 제외하여 이용하는 이 두 번째 방식을 선호하며 위에서 소개해드린

두 가지 방법 중 원하시는 방법을 선택하셔서 프로젝트를 추가로 생성하여 소스파일을 또 작성해야 하는 번거로움을 줄이시길 바랍니다.

반응형

+ Recent posts