연산자 Operator

이 글은 남궁성님의 자바의 정석 3/e을 기반으로 공부한 내용을 정리한 글입니다.

비교 연산자

비교 연산자는 두 피연산자를 비교하는 데 사용되는 연산자이다. 주로 조건문과 반복문의 조건식에 사용되며, 연산결과는 오직 true와 false 둘 중의 하나이다. 비교 연산자 역시 이항 연산자이므로 타입 다를 경우 자동 형변환을 한다.

4.1 대소비교 연산자 < > <= >=

두 피연산자의 크기를 비교하는 연산자이다. 참이면 ture, 거짓이면 false를 반환한다. 기본형 중에서 boolean형을 제외한 나머지 자료형에 다 사용할 수 있지만 참조형에는 사용할 수 없다.

4.2 등가비교 연산자 == !=

두 피연산자의 값이 같은지 다른지를 비교하는 연산자이다. 대소비교 연산자와 달리 모든 자료형에 사용할 수 있다. 기본형은 변수에 저장되어 있는 값을 비교하고, 참조형은 두 개의 피연산자(참조변수)가 같은 객체를 가리키고 있는지 알 수 있다. 기본형과 참조형을 서로 비교할 수 없다.

! 주의 ! 두 개의 기호로 이루어진 연산자(>=, <=)는 기호의 순서를 바꾸거나, 공백이 들어가서는 안 된다.

class Operator {
  public static void main(String[] args) {
    float f = 0.1f;

    System.out.println("0.1 == 0.1f  %b", 0.1 == 0.1f);
  }
}

위 예제를 실행하면 결과 값은 true가 아닌 false가 나온다. 왜냐하면 실수형은 정수형과 달리 근사값으로 저장되기 때문에 오차가 발생하는 것이다. double타입으로 형변환해도 마찬가지인데, 이는 정밀도가 더 높은 double타입으로 변환해도 부호와 지수는 달라지지 않고 가수의 빈자리만 0으로 채우기 때문이다.

float타입의 값과 double타입의 값을 비교하려면 double타입의 값을 float타입의 값으로 형변환하여 비교해야 한다.

문자열의 비교

두 문자열의 비교할 때는, 비교 연산자가 아닌 equals()라는 메서드를 사용해야 한다. 비교 연산자는 두 문자열이 완전히 같은 것인지 비교할 뿐이므로, 문자열의 내용이 같은지 비교하기 위해서는 equals()를 사용해야 한다.

equals()는 객체가 달라도 내용이 같으면 true를 반환한다. 대소문자 구분없이 비교하고 싶으면, equalsIgnoreCase()를 사용하면 된다.

위로

논리 연산자

논리 연산자는 둘 이상의 조건을 ‘그리고(AND)’나 ‘또는(OR)’으로 연결하여 하나의 식으로 표현할 수 있게 해준다. 논리 연산자는 피연산자로 boolean형 또는 boolean형 값을 결과로 하는 조건식만을 허용한다.

5.1 논리 연산자 - &&, ||, !

논리 연산자 &&는 ‘그리고(AND)’에 해당하며, 두 피연산자가 모두 true일 때만 true를 결과로 얻는다. ||는 ‘또는(OR)’에 해당하며, 피연산자 중 어느 한 쪽만 true이어도 true를 결과로 얻는다.

논리 연산자의 연산결과 표는 다음가 같다.

x y x || y x && y
true true true true
true false true false
false true true false
false false false false


논리 연산자를 사용할 때 주의할 점에 대한 몇 가지 예제가 있다.

x는 10보다 크고, 20보다 작다.

‘x > 10’와 ‘x < 20’가 ‘그리고(AND)’로 연결된 조건으로 다음과 같이 쓸 수 있다.

x > 10 && x < 20

‘x > 10’은 ‘10 < x’와 같으므로 가독성 측면에서 다음과 같이 쓸 수 있다.

10 < x && x < 20

단, 논리연산자를 생략하고 ‘10 < x < 20’처럼 쓸 수는 없다.

i는 2의 배수 또는 3의 배수이다.

어떤 수가 2의 배수라면 2로 나누었을 때 나머지가 0이다. 따라서 나머지연산의 결과가 0인지 확인하면 된다.

i % 2 == 0 || i % 3 == 0

i는 2의 배수 또는 3의 배수이지만 6의 배수는 아니다.

이전 조건에 6의 배수를 제외하는 조건이 추가되었다. 추가된 조건을 &&로 붙인다.

(i % 2 == 0 || i % 3 == 0) && i % 6 != 0

괄호를 사용한 이유는 &&||보다 우선순위가 높기 때문이다. 이처럼 &&||가 함께 사용된 경우 괄호를 사용해서 우선순위를 명확히 해주는 것이 좋다.

문자 ch는 숫자(‘0’ ~ ‘9’)이다.

입력된 문자가 숫자인지 확인하는 식은 다음과 같다.

'0' <= ch && ch <= '9'

유니코드에서 문자 ‘0’부터 ‘9’까지 연속으로 배치되어 있기 때문에 가능한 식이다.

문자 ch는 대문자 또는 소문자이다.

와 마찬가지로 연속적으로 배치되어 있으므로 식은 다음과 같다.

('a' <= ch && ch <= 'z') || ('A' <= ch && ch <= 'Z')

효율적인 연산(short circuit evaluation)

논리연산자의 특징은 효율적인 연산을 한다는 것이다.

OR연산 ||의 경우, 어느 한 쪽만 이어도 전체 연산결과가 이므로 좌측 피연산자가 true이면 우측 피연산자의 값은 평가하지 않는다.

AND연산 &&의 경우, 어느 한 쪽만 거짓이어도 전체 연산결과가 거짓이므로 좌측 피연산자가 false이면 우측 피연산자의 값은 평가하지 않는다.

따라서 같은 조건식이라도 피연산자의 위치에 따라 연산속도가 달라질 수 있는 것이다.

논리 부정 연산자 !

피연산자가 true이면 false, false이면 true를 결과로 반환하는 연산자이다. 이 연산자를 반복적으로 사용하면 참과 거짓이 차례대로 반복되는데 이를 이용하여 토글 버튼(toggle button)을 구현할 수 있다.

주로 사용되는 곳은 조건문과 반복문의 조건식이다.

5.2 비트 연산자 & | ^ ~ « »

비트 연산자는 피연산자를 비트단위로 논리 연산한다. 피연산자를 이진수 표현했을 때의 각 자리를 연산하며, 실수는 허용하지 않고 정수(문자 포함)만 허용된다. 비트연산에서도 피연산자의 타입을 일치시키는 ‘산술 변환’이 일어날 수 있다.

비트 OR연산자 |는 특정 비트의 값을 변경할 때 주로 사용한다.

비트 AND연산자 &는 특정 비트의 값을 뽑아낼 때 주로 사용한다.

비트 XOR연산자 ^는 두 피연산자의 비트가 다를 때만 1이 된다. 같은 값을 두고 연산을 수행하면 원래의 값으로 돌아오는 특징이 있어 간단한 암호화에 사용된다.

비트 전환 연산자 ~

이 연산자는 피연산자를 2진수로 표현했을 때 0은 1로, 1은 0으로 바꾼다. 논리 부정 연산자 !와 유사하다.

비트 전환 연산자 ~에 의해 ‘비트 전환’ 되고 나면, 부호있는 타입의 피연산자는 부호가 반대로 된다. 즉, 피연산자의 ‘1의 보수’를 얻을 수 있는 것이다.

쉬프트 연산자 << >>

이 연산자는 피연산자의 각 자리(2진수로 표현했을 때)를 오른쪽>> 또는 왼쪽<<으로 이동(shift)한다고 해서 ‘쉬프트 연산자(shift operator)’라고 부른다.

8 << 2는 10진수 8의 2진수를 왼쪽으로 2자리 이동한다. 과정은 다음과 같다.

10진수 8은 2진수로 `00001000`이다.
0 0 0 0 1 0 0 0

`8 << 2`은 10진수 8의 2진수를 왼쪽으로 2자리 이동시킨다.
0 0 0 0 1 0 0 0      

자리이동으로 인해 저장범위를 벗어난 값은 버려지고, 빈자리는 0으로 채워진다.
0 0 1 0 0 0 0 0

`8 << 2`의 결과는 2진수로 `00100000`이 된다.(10진수로 32)
0 0 1 0 0 0 0 0

<<연산자의 경우, 피연산자의 부호에 상관없이 각 자리를 왼쪽으로 이동시키며 빈칸을 0으로 채우면 된다.

>>연산자는 오른쪽으로 이동시키기 때문에 부호있는 정수는 부호를 유지하기 위해 왼쪽 피연산자가 음수인 경우 빈자리를 1로 채운다. 양수일 때는 0으로 채운다.

쉬프트 연산자는 다른 이항 연산자들과 달리 피연산자의 타입을 일치시킬 필요가 없기 때문에 좌측 피연산자는 산술변환이 적용되어 int보다 작은 타입은 int타입으로 자동 변환이 되지만 우측 피연산자에는 적용되지 않는다.

2진수 n자리를 왼쪽으로 이동하면 피연산자를 2n으로 곱한 결과를 얻고, 오른쪽으로 이동하면 2n으로 나눈 결과를 얻는다.

x << n 또는 x >> n에서, n의 값이 자료형의 bit수 보다 크면 자료형의 bit수로 나눈 나머지만큼만 이동한다. n은 정수만 가능하며 음수인 경우, 부호없는 정수로 자동 변환된다.

>> 또는 <<연산자를 사용한느 것이 곱셈이나 나눗셈 연산자를 사용하는 것보다 빠르다. 하지만 가독성은 상대적으로 떨어진다.

그 외의 연산자

6.1 조건 연산자 ? :

조건 연산자는 조건식, 식1, 식2 모두 세 개의 피연산자를 필요로 하는 삼항 연산자이며, 삼항 연산자는 조건 연산자 하나뿐이다.

조건 연산자는 첫 번째 피연산자인 조건식의 평가결과에 따라 다른 결과를 반환한다. 조건식의 평가결과가 true이면 식1, false이면 식2가 연산결과가 된다. 가독성을 위해 괄호로 조건식을 둘러싸는 경우도 있지만 필수는 아니다. 조건문인 if문으로 바꿔 쓸 수 있으며, if문 대신 조건 연산자를 사용하면 코드를 보다 간단히 할 수 있다.

중첩해서 사용하면 셋 이상 중 하나를 결과로 얻을 수 있다. 여러 번 중첩하면 코드가 간략해지지만, 가독성이 떨어지므로 필요한 경우에 한번 정도만 중첩하는 것이 좋다.

식1과 식2의 피연산자 타입이 다른 경우, 이항 연산자처럼 산술 변환이 발생한다.

6.2 대입 연산자 = op=

대입 연산자는 변수와 같은 저장공간에 값 또는 수식의 연산결과를 저장하는데 사용된다. 이 연산자는 오른쪽 피연산자의 값을 왼쪽 피연산자에 저장한다. 그리고 저장된 값을 연산결과로 반환한다.

대입 연산자는 연산자들 중에서 가장 낮은 우선순위를 가지고 있기 때문에 식에서 제일 나중에 수행된다. 또한 진행방향은 오른쪽에서 왼쪽이다.

lvalue와 rvalue

대입 연산자의 왼쪽 피연산자를 lvalue(left value), 오른쪽 피연산자를 rvalue(right value)라고 한다.

rvalue는 변수뿐만 아니라 식이나 상수 등이 모두 가능하지만, lvalue는 반드시 변수처럼 값을 변경할 수 있는 것이어야 한다. 따라서 리터럴이나 상수같이 값을 저장할 수 없는 것들은 lvalue가 될 수 없다.

복합 대입 연산자

대입 연산자는 다른 연산자(op)와 결합하여 op=와 같은 방식으로 사용될 수 있다. 예를 들어, i = i + 3i += 3과 같이 표현할 수 있다. 결합된 두 연산자는 공백없이 붙여 써야 한다.

op= =
i += 3; i = i + 3
i -= 3; i = i - 3
i *= 3; i = i * 3
i /= 3; i = i / 3
i %= 3; i = i % 3
i «= 3; i = i « 3
i »= 3; i = i » 3
i &= 3; i = i & 3
i ^= 3; i = i ^ 3
i |= 3; i = i | 3
i *= 10 + j; i = i * (10 + j)


위로