문자열
ASCII 코드 -> 소문자와 대문자 차이 32
65(A), 90(Z)
97(a), 122(z)
문자열 대문자 변환 ( Upper <-> Lower)
String str = "hello";
String upperStr = str.toUpperCase(); // "HELLO"
문자의 대문자 변환
char ch = 'a';
char upperCh = Character.toUpperCase(ch); // 'A'
문자의 소/대문자 확인
char c = 'a';
if (Character.isLowerCase(c)) {
System.out.println(c + "는 소문자입니다.");
}
문자열에서 문자 추출
String str = "hello";
char ch = str.charAt(0); // 'h'
문자열에서 특정 문자나 문자열이 처음으로 등장하는 위치의 인덱스 반환.(없으면 -1반환)
String str = "Hello, World!";
int index = str.indexOf('o'); // 4 (처음 'o'가 등장하는 위치)
문자열에서 특정 부분 문자열 추출
1. substring(int beginIndex):
String str = "Hello, World!";
String result = str.substring(7); // "World!"
문자열의 beginIndex부터 끝까지 부분 문자열을 반환
2. substring(int beginIndex, int endIndex):
String str = "Hello, World!";
String result = str.substring(0, 5); // "Hello"
문자열의 beginIndex부터 endIndex 직전까지 부분 문자열을 반환
문자열 특정 구분자 기준으로 나누어 배열로 반환
1. split(String regex):
String str = "apple,banana,orange";
String[] result = str.split(",");
// 결과: ["apple", "banana", "orange"]
주어진 정규식 regex를 기준으로 문자열을 나누어 배열로 반환합니다.
2. split(String regex, int limit)
String str = "apple,banana,orange";
String[] result1 = str.split(",", 2);
// 결과: ["apple", "banana,orange"] (2개만 나누고 나머지는 함께)
String[] result2 = str.split(",", -1);
// 결과: ["apple", "banana", "orange"] (모든 구분자로 나눔)
주어진 정규식 regex로 문자열을 나누되, limit에 따라 나눈 조각의 최대 개수를 지정(limit가 음수면 제한 없이 모든 구분자를 사용해 나눈다.)
3. 여러 구분자로 문자열 나누기
String str = "apple,banana;orange:grape";
String[] result = str.split("[,;:]");
// 결과: ["apple", "banana", "orange", "grape"]
영문자이거나 숫자인지 확인
char ch = 'a';
boolean result = Character.isLetterOrDigit(ch); // true
알파벳인지 확인
char ch = 'a';
boolean result = Character.isAlphabetic(ch); // true
숫자인지 확인
char ch = '5';
boolean result = Character.isDigit(ch); // true
문자열을 뒤집기
String reversed = new StringBuilder("god").reverse().toString(); // "dog"
직접 char로 비교하느것보다 속도가 더 느리다.
특정 문자열 변경
// "World"를 "Earth"로 대체
String str = "Hello, World! Welcome to the World!";
String result = str.replaceAll("World", "Earth"); // "Hello, Earth! Welcome to the Earth!"
특정 문자 변경
String s = "#Hel*o";
String tmp = s.replace('#', '1').replace('*', '0');
초기 문자열: "#Hel*o"
replace('#', '1'): "1Hel*o" (#를 1로 치환)
replace('*', '0'): "1Hel0o" (*를 0으로 치환)
배열을 문자열로 변경
char[] chars = {'a', 'b', 'c'};
String str = String.valueOf(chars); // "abc"
String을 배열로 변경
String str = "Hello";
char[] charArray = str.toCharArray();
// 결과: ['H', 'e', 'l', 'l', 'o']
전처리: 알파벳 소문자와 공백만 남기기
StringBuilder cleaned = new StringBuilder();
for (char c : p.toCharArray()) {
// 알파벳이면 그대로 추가, 공백이면 공백 추가, 그 외는 무시
if (Character.isLetter(c)) {
cleaned.append(Character.toLowerCase(c));
} else if (c == ' ') {
cleaned.append(c);
}
}
// 처리된 문자열을 단어로 나누기
String[] words = cleaned.toString().split(" ");
리스트를 String 배열로 변환해 리턴한다.
letterList.toArray(new String[0]);
리턴 타입이 명확하게 String[]이 되도록 한다.
Char배열과 String 클래스의 변환
String 클래스는 char 배열에 기능(메서드)를 추가한 것이다.
char[] 배열을 String으로 변환
public class CharArrayToStringExample {
public static void main(String[] args) {
char[] charArray = {'H', 'e', 'l', 'l', 'o'};
String str = new String(charArray);
System.out.println("String: " + str); // 출력: Hello
}
}
String을 char[] 배열로 변환
public class StringToCharArrayExample {
public static void main(String[] args) {
String str = "Hello";
char[] charArray = str.toCharArray();
// char[] 배열 출력
System.out.println("charArray: " + java.util.Arrays.toString(charArray));
// 출력: [H, e, l, l, o]
}
}
문자열 클래스
자바에서는 문자열과 관련된 주요 클래스는 아래와 같다.
클래스 | 설명 |
String | 문자열을 저장하고 조작할 때 사용 |
StringBuilder | 효율적인 문자열 조작 기능이 필요할 떄 사용 |
StringTokenizer | 구분자로 연결된 문자열을 분리할 떄 사용 |
StringBuilder
String은 내부 문자열을 수정 할수 없다.
아래 코드를 보면 다른 문자열을 결합해서 내부 문자열을 변경하는 것 처럼 보이지만 사실 'ABCDEF'라는 새로운 String 객체를 생성하는 것이다. 그리고 data 변수는 새로 생성된 String 객체를 참조하게 된다.
String data ="ABC";
data+= "DEF";
문자열 + 연산은 새로운 String 객체가 생성되고 이전 객체는 계 버려지는 것이기 떄문에 효율이 좋지 않다.
StringBuilder는 내부 버퍼(데이터를 저장하는 메모리)에 문자열을 저장해두고 그 안에서 추가, 수정, 삭제 작업을 하도록 설계되어 있다.
따라서 String 처럼 새로운 객체를 만들지 않고도 문자열을 조작할 수 있다.
StringBuilder가 제공하는 조작 메소드는 아래와 같다.
public class StringBuilderExample {
public static void main(String[] args) {
String data = new StringBuilder()
.append("DEF")
.insert(0, "ABC")
.delete(3, 4)
.toString();
System.out.println(data);
}
}
StringTokenizer
문자열이 구분자(delimiter)로 연결되어 있을 경우, 구분자를 기준으로 문자열을 분리하려면 String의 split() 메소드를 이용하거나 java.util 패키지의 StringTokenizer 클래스를 이용할 수 있다.
split은 정규표현식으로 구분하고, StringTokenizer는 문자로 구분한다.
String data1 = "홍길동&이수홍,박연수";
String[] arr = data1.split("&|,");
위와 같은 여러 종류가 아닌 한 종류의 구분자만 있다면 StringTokenizer를 사용할 수 있다.
String data2 = "홍길동/이수홍/박연수";
StringTokenizer st = new StringTokenizer(data2, "/");
StringTokenizer객체를 생성할 때는 첫 번째 매개값으로 전체 문자열을 주고, 두 번쨰 매개 값으로 구분자를 주면된다.
구분자를 생략하면 공백이 기본 구분자가 된다.
StringTokenizer객체가 생성되면 아래 메소드들을 이용해서 분리된 문자열을 얻을 수 있다.
리턴타입 | 메소드(매개변수) | 설명 |
int | countTokens() | 분리할 수 있는 문자열의 총 수 |
boolean | hasMoreTokens() | 남아 있는 문자열이 있는지 여부 |
String | nextToken() | 문자열을 하나씩 가져옴 |
nextToken()은 더이상 가져올 문자열이 없으면 예외를 발생시킨다.
그래서 nextToken()을 사용하기 전에 hasMoreTokens()메소드로 가져올 문자열이 있는지 먼저 조사하는게 좋다.
while (st.hasMoreTokens()) {
String token = st.nextToken();
System.out.println(token);
}
포장 클래스
포장 객체를 생성하기 위한 클래스는 java.lang 패키지에 포함되어 있으며, Character와 Integer만 제외하고 모두 기본타입의 첫문자를 대문자로 바꾼 이름을 갖는다.
포장 객체는 단지 객체로 생성하는데 목적이 있으며, 컬렉션 객체는 기본 타입의 값은 저장할 수 없고 객체만 저장할 수 있기 때문이다.
박싱 : 기본 타입의 값을 포장 객체로 만드는 과정
언박싱 : 포장 객체에서 기본타입의 값을 얻어내는 과정
Interger obj = 100; // 박싱
int value = obj; // 언박싱
int value = obj + 50; // 언박싱 후 연상
대부분의 포장 클래스에는 'parse+기본타입'명으로 되어 있는 정적 메소드가 있어 문자열을 해당 기본타입으로 변환한다.
public class ValueCompareExample {
public static void main(String[] args) {
//-128~127 초과값일 경우
Integer obj1 = 300;
Integer obj2 = 300;
System.out.println("==: " + (obj1 == obj2)); //fasle
System.out.println("equals(): " + obj1.equals(obj2)); //true
System.out.println();
//-128~127 범위값일 경우
Integer obj3 = 10;
Integer obj4 = 10;
System.out.println("==: " + (obj3 == obj4)); //true
System.out.println("equals: " + obj3.equals(obj4)); //true
}
}
포장 객체는 내부 값을 비교하기 위해 ==와 != 연산자를 사용할 수 없다.
이 연산자는 내부의 값을 비교하는 것이 아니라 포장 객체의 번지를 비교하기 때문이다.
예를 들어 다음 두 Integer 객체는 300이라는 동일한 값을 갖고 있지만 == 연산의 결과는 false가 나온다.
포장 객체는 비교할때 equals() 메소드를 사용하자.
예외도 있다.
포장 객체의 효율적 사용을 위해 다음 범위의 값을 갖는 포장 객체는 공유(캐시)된다.
이 범위의 값을 갖는 포장 객체는 ==와 != 연산자로 비교할 수 있지만, 내부 값을 비교하는 것이 아니라 객체 번지를 비교한다는 것을 알아야 한다.
byte, short, int -> -128 ~ 127
포장 객체에 정확히 어떤 값이 저장될 지 모르는 상황이라면 ==과 !=는 사용하지 않는 것이 좋다.
대신 equals() 메소드로 내부 값을 비교할 수 있다.
포장 클래스의 equals() 메소드는 내부의 값을 비교하도록 재정의되어 있다.
배열
1차원 배열
배열 변수 선언
타입[] 변수;
int[] arrInt.
타입 변수[];
int arrInt[];
값 목록으로 배열 생성
타입[] 변수 = {값1, 값2, 값3,...}
String[] season = {"봄", "여름", "가을", "겨울"}
배열 변수를 미리 선언한 후에는 값 목록을 변수에 대입할 수 없다.
타입[] 변수;
변수 = {"봄", "여름", "가을", "겨울"}; // 컴파일 에러
배열 변수를 선언한 시점과 값 목록이 대입되는 시점이 다르다면 아래와 같이 new 타입[]를 중괄호 앞에 붙여야한다.
변수 = new 타입[] {값0,값1,값2,/..};
new 연산자로 배열 생성(값의 목록은 없지만 향후 값들을 저장할 목적)
타입[] 변수 = new 타입[길이[;
int[] arrInt =new int[10];
다차원 배열
값 목록으로 다차원 배열 생성
int [][] scores = {
{ 80, 90, 96 }, // 1차원 배열의 0 인덱스: 첫 번째 반 성적
{ 76, 88 } // 1차원 배열의 1 인덱스: 두 번째 반 성적
};
int score = scores[0][2]; // 96
int score = scores[1][1]; // 88
scores.length // 반의 수: 2
scores[0].length // 첫 번째 반의 학생 수: 3
scores[1].length // 두 번째 반의 학생 수: 2
new 연산자로 다차원 배열 생성
int[][] scores =new int[2][3];
int[][] scores=new int[2][];
scores[0]=new int[3];
scores[1]=new int[2];
배열 정렬
배열 오름차순 정렬
int[] numbers = {5, 2, 8, 3, 1};
Arrays.sort(numbers);
System.out.println(Arrays.toString(numbers)); // [1, 2, 3, 5, 8]
배열 내림차순 정렬
int[] numbers = {5, 2, 8, 3, 1};
// int[]를 Integer[]로 변환
Integer[] integerNumbers = Arrays.stream(numbers).boxed().toArray(Integer[]::new);
Arrays.sort(integerNumbers, Collections.reverseOrder());
System.out.println(Arrays.toString(integerNumbers)); // [8, 5, 3, 2, 1]
Arrays.sort(numbers);// 오름차순 정렬
// 배열 뒤집기
for (int i = 0; i < numbers.length / 2; i++) {
int temp = numbers[i];
numbers[i] = numbers[numbers.length - 1 - i];
numbers[numbers.length - 1 - i] = temp;
}
System.out.println(Arrays.toString(numbers)); // [8, 5, 3, 2, 1]
// 배열의 각 요소에 -1을 곱하기
for (int i = 0; i < numbers.length; i++) {
numbers[i] *= -1;
}
// 오름차순 정렬 (음수 상태로)
Arrays.sort(numbers);
// 다시 -1을 곱해서 원래 숫자로 복원 (결과적으로 내림차순)
for (int i = 0; i < numbers.length; i++) {
numbers[i] *= -1;
}
System.out.println(Arrays.toString(numbers)); // [8, 5, 3, 2, 1]
2번 방법이 제일 효율적이다.
스트림 사용한 중복 제거와 내림차순 배열
private static int[] solution(int[] arr) {
// ❶ 중복값 제거
Integer[] result = Arrays.stream(arr).boxed().distinct().toArray(Integer[]::new);
System.out.println(result);
Arrays.sort(result, Collections.reverseOrder()); // ❷ 내림차순 정렬
// int형 배열로 변경 후 반환
return Arrays.stream(result).mapToInt(Integer::intValue).toArray();
}
스트림이 아니라 TreeSet으로도 가능.
private static int[] solution(int[] arr) {
Set<Integer> set = new TreeSet<>(Collections.reverseOrder()); // TreeSet으로 중복 제거와 동시에 내림차순 정렬
for (int num : arr) {
set.add(num); // 중복 제거 및 자동 정렬
}
return set.stream().mapToInt(Integer::intValue).toArray(); // int형 배열로 변환 후 반환
}
큰 데이터에서는 TreeSet이 유리하다.
배열 복사
가장 기본적인 for문을 이용한 복사
public static void main(String[] args) {
//길이 3인 배열
int[] oldIntArray = { 1, 2, 3 };
//길이 5인 배열을 새로 생성
int[] newIntArray = new int[5];
//배열 항목 복사
for(int i=0; i<oldIntArray.length; i++) {
newIntArray[i] = oldIntArray[i];
}
//배열 항목 출력
for(int i=0; i<newIntArray.length; i++) {
System.out.print(newIntArray[i] + ", ");
}
}
System.arraycopy()를 이용한 얕은 복사
배열은 한번 생성하면 길이를 변경할 수 없다.
더 많은 저장 공간이 필요하다면 더 큰 길이의 배열을 새로 만들고 이전 배열로 부터 항목들을 복사해야한다.
public static void main(String[] args) {
//길이 3인 배열
String[] oldStrArray = { "java", "array", "copy" };
//길이 5인 배열을 새로 생성
String[] newStrArray = new String[5];
//배열 항목 복사
System.arraycopy( oldStrArray, 0, newStrArray, 0, oldStrArray.length);
//배열 항목 출력
for(int i=0; i<newStrArray.length; i++) {
System.out.print(newStrArray[i] + ", ");
}
}
for문으로 하나씩 복사하는 방식 이외에 위처럼 한줄로 복사하는것이 효율적이다.
1차원 배열에서 clone은 깊은 복사, 2차원 배열에서 clone은 얕은 복사
1차원 배열 clone 복사(기본형, 깊은복사)
int[] original = {1, 2, 3};
int[] cloned = original.clone();
cloned[0] = 10; // 복사된 배열의 첫 번째 요소 변경
System.out.println(Arrays.toString(original)); // [1, 2, 3]
System.out.println(Arrays.toString(cloned)); // [10, 2, 3]
2차원 배열 clone 복사 (기본형, 얕은 복사) : 객체 참조를 복사 (참조형 데이터의 경우 동일 객체를 참조).
int[][] original = {{1, 2}, {3, 4}};
int[][] cloned = original.clone();
cloned[0][0] = 10; // 복사된 배열의 첫 번째 요소 변경
System.out.println(Arrays.deepToString(original)); // [[10, 2], [3, 4]]
System.out.println(Arrays.deepToString(cloned)); // [[10, 2], [3, 4]]
- 1차원 배열에서는 clone()으로 완전한 깊은 복사가 이루어진다.
- 다차원 배열에서는 얕은 복사가 이루어지며, 내부 배열은 여전히 원본 배열과 연결된다.
2차원 배열 clone 복사 (기본형, 깊은 복사) : 객체의 값을 새로 복사하여 독립적인 객체를 생성.
int[][] original = {{1, 2}, {3, 4}};
// 2차원 배열의 깊은 복사
int[][] deepCopied = new int[original.length][];
for (int i = 0; i < original.length; i++) {
deepCopied[i] = original[i].clone(); // 각 내부 배열을 개별적으로 복사
}
// 복사된 배열 수정
deepCopied[0][0] = 10;
System.out.println(Arrays.deepToString(original)); // [[1, 2], [3, 4]]
System.out.println(Arrays.deepToString(deepCopied)); // [[10, 2], [3, 4]]
특징 | 배열.clone() | System.arraycopy |
복사 범위 | 배열 전체 | 부분 또는 전체 지정 가능 |
목적지 배열 | 자동 생성 (복사 후 반환) | 미리 생성해야 함 |
다차원 배열 | 얕은 복사 (내부 배열 참조 복사) | 얕은 복사 (내부 배열 참조 복사) |
성능 | 일반적으로 더 느림 (특히 대량 복사 시) | 더 빠름 (네이티브 메서드로 최적화) |
코드 간결성 | 간단 (전체 복사에 적합) | 복사 범위와 위치 지정 필요 |
사용 용도 | 배열 전체를 복사 | 배열 일부 또는 특정 위치에 복사 |
특이한 경우: 참조형 데이터(참조형, 깊은복사)
class Person {
String name;
public Person(String name) {
this.name = name;
}
@Override
public String toString() {
return name;
}
}
public class DeepCopyWithObjectsExample {
public static void main(String[] args) {
Person[][] original = { // 원본 2차원 배열
{new Person("Alice"), new Person("Bob")},
{new Person("Charlie"), new Person("Dave")}
};
Person[][] deepCopied = deepCopy(original);
deepCopied[0][0].name = "Zoe";
System.out.println("원본 배열:");
print2DArray(original); // 원본 배열은 영향을 받지 않음
System.out.println("깊은 복사된 배열:");
print2DArray(deepCopied); // 복사된 배열만 변경됨
}
public static Person[][] deepCopy(Person[][] array) {
Person[][] copiedArray = new Person[array.length][];
for (int i = 0; i < array.length; i++) {
copiedArray[i] = new Person[array[i].length];
for (int j = 0; j < array[i].length; j++) {
copiedArray[i][j] = new Person(array[i][j].name); // 새 객체 생성
}
}
return copiedArray;
}
public static void print2DArray(Person[][] array) {
for (Person[] row : array) {
System.out.println(Arrays.toString(row));
}
}
}
'모카스터디 > Java' 카테고리의 다른 글
연산자와 반복문과 조건문 (0) | 2024.10.01 |
---|---|
변수와 기본타입과 타입변환(캐스팅) (0) | 2024.09.30 |
자바의 특징 및 자바 프로그램 실행과정 (1) | 2024.09.30 |