본문 바로가기
Java

연산자, for 문, 배열과 String 메소드

by 스니펫 2023. 10. 19.

연산자

프로그래밍에서 변수와 상수를 계산하기 위해 연산을 표현하는 기호

  • 연산자 : 계산할 기호 (+, -, ÷, …)
  • 피연산자 : 연산자로 인해 계산되는 숫자

연산자 우선순위

시프트 연산자

  • << : 왼쪽 값을 오른쪽 값만큼 비트를 왼쪽으로 이동, 1비트 이동시킬 때마다 2배씩 증가
  • >> : 왼쪽 값에 오른쪽 값만큼의 부호 비트를 채우면서 오른쪽으로 이동, 1비트 이동시킬 때마다 2배씩 감소
int x = 11;
printf("%d\n", x<<3); // 88 출력
printf("%d", x>>1); // 5 출력

// 11의 2진수는 1011
// 왼쪽으로 3비트 이동하면 1011000 이 됨
// 오른쪽으로 1비트 이동하면 101 이 됨

비트 연산자

  • ^ : 두 값을 비트 연산하여 같은 비트의 값이 서로 다르면 해당 비트 값이 1이 되고, 그렇지 않으면 0이 되는 연산자(XOR 연산자)
  • ~ : 모든 비트의 값을 반대로 바꾸는 반전 기능을 하는 연산자(NOT 연산자), 뒤바뀐 Sign Bit에 의해 연산 결과로 부호를 반대로 바꾼 값에 1을 뺀 값이 나옴
printf("%d", ~12); // -13 출력
더보기

부호비트(Sign Bit / MSG;Most Significant Bit)

컴퓨터가 정수를 표현하는데 있어서 가장 왼쪽에 위치하는 첫 번째 비트인 MSB는 부호를 표현하는데 사용된다. MSB의 값이 0인 경우 양수를 나타내고, 값이 1인 경우 음수를 나타낸다.

음의 정수 표현방법

정수 5가 00000101로 저장되어 있을 때 음의 정수 -5는 MSB만 1로 설정한 10000101이 될 것 같지만, 이 둘을 더해보면 그 결과가 0이 아닌 것을 알 수 있다. ( 00000101 + 10000101 = 10001010 )

1. 1의 보수법 : 1의 보수법은 해당 양수의 모든 비트를 반전하여 음수를 표현하는 방법이다. 하지만 부호 비트와 절댓값 방법과 같이 +0과 -0이 따로 존재하는 문제점을 가진다.

 

2. 2의 보수법 : 2의 보수법은 해당 양수의 모든 비트를 반전한 1의 보수에 1을 더하여 음수를 표현하는 방법이다. 이 방법은 1의 보수법이 두 개의 0을 가지는 문제점(+0과 -0)을 해결하기 위해 고안되었다. 이 방법을 사용하면 -0은 2의 보수를 구하는 과정에서 최상위 비트를 초과한 오버플로우가 발생하여 +0이 된다. 따라서 2의 보수법에서는 단 하나의 0만이 존재하게 된다. 현재 대부분의 시스템은 2의 보수법으로 음수를 표현하고 있다.

+) 12가 컴퓨터에 00001100 로 저장되었다면 ~12는 11110011이 되며, 이를 10진법으로 바꾸고 싶다면 똑같이 2의 보수법을 사용해보면 된다. 11110011의 2의 보수는 00001101이므로 2³+2²+1 = 13, 따라서 11110011는 -13이다.


반복문 - for 문

for( 초기식; 조건문; 증감식 ) { 명령문; } 

  1. 초기식에 따라 초기화 수행
  2. 조건식이 참일 경우 명령문 실행
  3. 명령문이 끝나고 돌아오면 증감식에 의해 값을 변경
  4. 조건식이 거짓일 경우까지 2~3번 반복… 거짓이면 for 문을 종료

향상된 for 문

for( 변수타입 변수명 : 배열) {  명령문;  }

연속된 변수 목록을 출력할 때 쓰임, 변수 타입과 변수명은 for 문안에서 연산을 수행할 변수를 정의

// 향상된 for 문
int[] numbers = {3,6,9,12,15};
for(int num: numbers) {
System.out.print(num + " ");
}
// 출력 : 3 6 9 12 15

배열 메소드 정리

1. 초기화

  • Arrays.fill 메소드 : 배열의 주소를 모두 같은 값으로 초기화한다. Arrays 클래스는 자바에서 기본으로 제공하는 메소드가 담긴 클래스이다.
Arrays.fill(arr, 1); // 배열 arr의 모든 값을 1로 초기화

2. 복사 : clone() 메소드

  • 얕은 복사 : 배열은 참조형 변수이며 실제값이 아닌 실제값의 주소값(참조값)을 가지기 때문에 배열 변수간에 대입 연산자 = 를 사용해 복사를 하게 되면 주소값만 복사된다. 이렇게 주소값만 복사되고 실제값은 1개로 유지되는 것을 얕은 복사라고 한다.
// 얕은 복사
int[] a = { 1, 2, 3, 4 };
int[] b = a; // 얕은 복사
b[0] = 3; // b 배열의 0번째 순번값을 3으로 수정 (1 → 3)
System.out.println(a[0]); // 출력 3 ← a 배열의 0번째 순번값도 3으로 조회
  • 깊은 복사 : 실제값을 새로운 메모리 공간에 복사한다.
// 깊은 복사
int[] a = { 1, 2, 3, 4 };
int[] b = new int[a.length];
for (int i = 0; i < a.length; i++) {
	b[i] = a[i]; // 깊은 복사
}
b[0] = 3; // b 배열의 0번째 순번값을 3으로 수정 (1 → 3)
System.out.println(a[0]); // 출력 1 ← 깊은 복사를 했기때문에 a 배열은 그대로


// 깊은 복사 메소드
// 1. clone() 메서드
int[] a = { 1, 2, 3, 4 };
int[] b = a.clone(); // 가장 간단한 방법
// 하지만, clone() 메서드는 2차원이상 배열에서는 얕은 복사로 동작함

// 2. Arrays.copyOf() 메서드
int[] a = { 1, 2, 3, 4 };
int[] b = Arrays.copyOf(a, a.length); // 원본 배열과 함께 length값(복사할 길이)도 같이 넣어줌
// 2+. Arrays.copyOfRange() 메서드 
// 0 ~ (a.length - 1) 까지 배열 복사
int[] c = Arrays.copyOfRange(a, 0, a.length); // copyOfRange(원본 배열, 복사할 시작 인덱스, 끝 인덱스)
더보기

배열 복사 메소드 : Arrays.copyOf() vs System.arraycopy()

  • System.arraycopy(원본배열, 원본 배열의 복사 시작 지점, 복사할 배열, 복사할 배열의 복사 시작 지점, 복사할 요소의 개수)
int[] arr = {1,2,3,4,5,6,7,8,9,10};
int[] copied = new int[15];

System.arraycopy(arr,0,copied,1,10)
System.out.println(Arrays.toString(copied)); 
// [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 0, 0, 0, 0] 남은 공간은 0으로 채워짐

Arrays.copyOf() 는 System.arraycopy() 메서드를 래핑한 함수로, System.arraycopy()가 구현하는 것 외에 추가 기능을 제공한다. ( System.arraycopy() 는 단순히 원본배열에서 대상으로 값을 복사하지만, Arrays.copyOf() 는 새로운 배열도 생성할 수 있고 필요한 경우 내용을 자르거나 채울 수 있음)

3. length() 메소드 : 해당 배열의 길이값을 응답해준다. 

String[] sArr = {"val1", "val2", "val3"}; 
for(int i = 0; i < sArr.length; i++){
	// 배열 sArr 길이만큼 반복
}

char [] cha1 = {'a', 'b', 'c', 't'}; // 정적할당
char [] cha2 = {'d', 'e', 'f'};
System.out.println(cha1.length); // 4 출력
System.out.println(cha2.length); // 3 출력
char [] cha3 = new char [cha1.length + cha2.length]; // 동적할당
System.out.println(cha3.length); // 7 출력

4. 형변환 : char[] → String

char [] cha1 = {'a', 'b', 'c', 't'};
char [] cha2 = {'d', 'e', 'f'};

// char[]을 스트링으로 형변환 (char[] → String)
String str1 = Arrays.toString(cha1); // 전달된 Object 값이 null이면 NullPointerException를 발생시키고 Object에 담긴 값이 String이 아니여도 출력
String str2 = String.valueOf(cha2); // 전달된 Object 값이 null이면 문자열 “null”을 만들어 반환

System.out.println(cha1 + str1); // cha1의 주소값과 [a, b, c, t] 출력
System.out.println(str2); // def 출력

// NPE를 방지하기 위해 toString보다는 valueOf를 사용하는 것을 추천

문자열 String

String = char[] 이기 때문에 String과 char 배열 둘다 문자열을 저장할 수 있는 변수다. 하지만 참조형 변수가 더 많은 기능을 가지고 있기 때문에 String을 더 많이 사용한다.

문자열 연산

문자열과 문자열, 문자열과 정수, 문자열과 실수를 더하게 되면 문자열이 된다.

String str = "I Love You. ";
System.out.println(str + "very much"); // I love you. very much 출력

System.out.println("5 + 2 = " + 3 + 4); // 5 + 2 = 34 출력
System.out.println("5 + 2 = " + 3.1 + 4); // 5 + 2 = 3.14 출력
System.out.println("5 + 2 = " + (3 + 4)); // 5 + 2 = 7 출력, (괄호)가 우선순위를 가져간다.
System.out.println(5 + 2 + " = 3 + 4"); // 7 = 3 + 4출력

String 메소드

1. length() : 리턴 타입은 int, 문자열의 길이를 반환한다.

  • 배열명.length는 배열의 길이를 반환하고, 문자열명.length()는 문자열의 길이를 반환함에 주의
String str = "ABCD";
int strLength = str.length();
System.out.println(strLength); // 4 출력

2. charAt(int index) : 리턴 타입은 char, 문자열에서 해당 index의 문자를 반환한다.

3. substring(int from, int to) : 리턴 타입은 String, 문자열에서 해당 범위(from ~ (to - 1))에 있는 문자열을 반환한다.

String str = " I Love You. ";

System.out.println(str.charAt(1)); // 'I' 출력
System.out.println(str4.substring(3, 7)); // "Love" 출력

4. equals(String str) : 리턴 타입은 boolean, 문자열의 내용이 같은지 확인한다. 같으면 결과는 true, 다르면 false가 된다.

String str = "ABCD";
String newStr = "ABCD"; // str 값과 같은 문자열 생성
boolean strEqual = newStr.equals(str);
System.out.println(strEqual); // true 출력

5. toCharArray() : 리턴 타입은 char[], 문자열을 문자배열( char[] )로 변환해서 반환한다.

6. new String(char[] charArr) : 리턴 타입은 String, 문자배열( char[] )을 받아서 String으로 복사해서 반환한다.

String str = "Hello, world!";
char[] arr = str.toCharArray(); // 문자열 str을 문자배열로 변환하고 문자배열 arr 초기화
		
for(int i=0; i<arr.length; i++) {
	System.out.printf(arr[i]); // Hello, world! 출력
}

String s = new String(arr); // 문자배열 arr을 받아서 String으로 복사해서 반환
System.out.printf(s); // Hello, world! 출력

7. split(String regex) : 리턴 타입은 String[], 문자열을 구분하기 위한 정규 표현 regex를 기준으로 문자열을 쪼개서 반환한다. 쪼갰는데 아무것도 없다면 빈 공백을 반환한다.

String str = "one;;three;four;five";
String word1 = str.split(";")[0];
String word2 = str.split(";")[1];

System.out.println("첫번째 단어:" + word1); // "첫번째 단어:one"
System.out.println("두번째 단어:" + word2); // "두번째 단어:"


String[] word3 = str.split(";", 3) // split(String regex, int limit)

for (String wo : words3 ){
	System.out.println(wo);
}
// words3[0] : "one"
// words3[1] : ""
// words3[2] : "three;four;five"

8. replace(CharSequnce target, CharSequence replacement) : 리턴 타입은 String, 모든 target을 replacement로 치환하여 반환한다.

9. replaceAll(String regex, String replacement) : replace와 비슷하지만 첫번째 인자로 특정 규칙을 가진 문자열의 집합인 [정규표현식] 을 넣을 수 있다.

10. replaceFirst(CharSequnce target, CharSequence replacement) : 맨 처음 발견된 target만 replacement로 치환하여 반환한다.

String str = "abc ABa 12345"
System.out.println(str4.replace("a", "x")); // "xbc ABx 12345", 모든 a가 x로 change됨
System.out.println(str4.replaceAll("[a-z]", "x")); // "xxx ABx 12345", 모든 a~z의 값이 x로 change됨 (replace와 다르게 특정 규칙을 가진 문자열의 집합인 [정규표현식] 사용 가능)
System.out.println(str4.replaceFirst(" ", "x")); // "abcxABa 12345", 맨 첫번째 스페이스만 x로 change

'Java' 카테고리의 다른 글

프로그래머스) 푸드 파이트 대회  (6) 2023.12.06
컬렉션  (0) 2023.10.20
래퍼 클래스, 참조형 변수, 형 변환  (0) 2023.10.17
JVM, Java 실행 과정, 변수  (1) 2023.10.13
문자열 비교  (0) 2023.10.02