○ 들어가는 글
이번 글은 원래 자바를 공부하거나, 혹은 추후에 사용할 때 헷갈리는 부분을 한번에 보기 위해서 정리해 놓은 노트였습니다. 따라서 비공개 였으나, 계속 쓰다 보니 양이 좀 되는 것 같아서 따로 하나의 문서로 만들게 되었습니다.
저는 쉽게 쉽게, 자바를 사용하다가 햇갈리는 부분이 있으면 그 때 그 때 참고하려고 합니다. 자주쓰는 코드나, 패키지 등을 확인하는데 유용할 것입니다.
예시코드는 아래 웹 컴파일러를 기준으로 작성하였습니다.
https://replit.com/languages/java10
Java Online Compiler & Interpreter
Write and run Java code using our Java online compiler & interpreter. You can build, share, and host applications right from your browser!
replit.com
1. 배열
○ 배열은 사실 기초가 되는 자료구조이자, 심화적으로 보면 신셩쓸 것도 많은 것은 사실입니다만, 우선 여기에서는 간단히만 넘어가려고 합니다. 이 부분은 따로 글을 쓸 수 있을 때 써 보도록 하죠. 우선 간단히 자주 쓰는 것들만 적어 놓았습니다.
- 배열을 출력할 때는 Array.toString() 메서드나, Array.deepToString() 을 사용할 수 있다.
System.out.println(Arrays.toString(StringArr));
- copyOf, copyOfRange도 사용 가능 -> 배열을 그냥 대입식면 참조된다. 따라서 위와 같이 복사 해야 한다.
int[] temp = Arrays.copyOf(array, arrayLength);
int[] temp = Arrays.copyOfRange(array, startIdx, endIdx);
2. 문자열 - String, StringBuilder, StringJoiner
○ 스트링도 어떻게 보면 자료구조라고 볼 수 있을 것 입니다. 문자의 배열 버전이라고 볼 수 있으니까요. 다만 String 은 결국 정적 이기때문에 자료구조로 사용하기에는 조금 부족한 감이 있습니다. 따라서, 여기에서는 자료구조로 사용하기 위한 StringBuilder를 기준으로 정리하려고 합니다. StringBuilderd은 쉽게 생각하면 Mutable 한 String이라고 생각하시면 될 것입니다. 수정이 가능하지요.
- 기본적으로 String은 클래스이다.
- StringBuilder와 StringJoiner를 활용할 수 있다. String과 동일하지만, 내부를 변경이 가능하다.
- StringBuilder
- StringBuilder 생성 : StringBuilder str = new StringBuilder()
- Stringbuilder 에 문자열 추가 : StringBuilder.append()
- StringJoiner
- StringJoiner 생성 : StringJoiner strJoiner = new StringJoiner( -Split String- )
- StringJoiner 에 문자열 추가 : StringJoiner.add()
- StringBuilder
- 그 외 유용한 String클래스의 메서드 들이 존재한다.
- String.join() - 배열을 구분자를 기준으로 String 으로 결합
- String.split() - String을 구분자를 기준으로 배열로 분리
- String.charAt(Idx) - String의 요솟값에 접근
- String.substring(start_Idx , end_Idx) - String중 범위 지정한 String을 반환
import java.util.Arrays;
import java.util.StringJoiner;
class Main {
public static void main(String args[]) {
//1
String str = "hello world!";
System.out.println(str);
//2
String[] strArr = {"Hello","World","!!"};
StringBuilder strBuilder = new StringBuilder();
StringJoiner strJoiner = new StringJoiner(" ");
for(String i : strArr){
strBuilder.append(i);
}
for(String i : strArr){
strJoiner.add(i);
}
System.out.println(strBuilder);
System.out.println(strJoiner);
//3
String str2 = "hello java!!";
String[] strArr2 = {"apple","banana","cat"};
String strToJoin = String.join("",strArr2);
String[] strToSplit = str2.split(" ");
System.out.println(strToJoin);
System.out.println(Arrays.toString(strToSplit));
System.out.println(str2.substring(3,7) );
System.out.println(str2.charAt(0));
}
}
3. List(arrayList) 활용
○ 자바의 컬렉션 중 하나인 List입니다. 기본적으로 다른 언어에서 볼 수 있는 List와 비슷합니다. Kotlin에서 설명한 것과 크게 다른 것은 없습니다. 인터페이스 구조는 여기서는 다루지 않겠습니다.
- List1.add(Item) - 리스트에 추가시에 사용된다.
- List1.get(Item) - 리스트에 참조시에 사용된다.
- List1.contains(Item) - 리스트에 항목 존재 여부 참조시에 사용된다.
- List1.indexOf(Item) - 리스트에 항목의 인덱스 참조에 사용된다.
- List1.add(Item) - 리스트에 추가시에 사용된다.
- List1.sort(null) - 리스트에 정렬시 사용된다.
import java.util.Arrays;
import java.util.ArrayList;
import java.util.Collections;
class Main {
public static void main(String args[]) {
ArrayList<Integer> list = new ArrayList<Integer>(Arrays.asList(10,7,8,1));
System.out.println(list);
list.sort(null);
Collections.sort(list);
System.out.println(list);
list.add(5);
list.add(null);
list.add(0,3);
System.out.println(list);
System.out.println(list.get(0));
System.out.println(list.contains(5));
System.out.println(list.indexOf(5));
System.out.println(list);
list.remove(1);
list.clear();
System.out.println(list);
}
}
4. set의 활용
○ 이 또한 크게 다른 점은 없습니다. 요소간의 중복을 허용하지 않지요. 간단히만 짚고 넘어가겠습니다. 개인적으로 자주쓰는 컬렉션중 하나입니다. 집합연산이 의외로 많이 쓰이지요.
- 마땅히 변환활 수 없을 때에는 for문으로 일일히 수행해야 한다.
- 자바에서 for문은 아래처럼 지원하기도 한다.
- hashSet은 합집합, 교집합등등을 지원하기도 한다. kotlin에서는 연산기호로 가능했지만 여기에서는 특정 메서드를 사용해야 한다.
- set1.addAll(set2) - 합집합이 반환된다.
- set1.retainAll(set2) - 교집합이 반환된다.
- set1.removeAll(set2) - 차집합이 반환된다.
- set1.containsAll(set2) - 부분 집합 여부 -> 이는 참 거짓이 반환된다.
- set1.add(item) - 요소를 추가할 때 사용한다.
- set1.remove(item) - 요소를 지울 때 사용한다.
- set1.sorted() - 요소를 정렬하는데 사용된다.
import java.util.Arrays;
import java.util.HashSet;
class Solution {
public int solution(int n, int[] lost, int[] reserve) {
HashSet<Integer> reserveNum = new HashSet<>();
HashSet<Integer> lostNum = new HashSet<>();
HashSet<Integer> retainNum = new HashSet<>();
for( int i : lost ){
lostNum.add(i);
}
for( int i : reserve ){
reserveNum.add(i);
}
retainNum.addAll(lostNum);
retainNum.retainAll(reserveNum);
reserveNum.removeAll(retainNum);
lostNum.removeAll(retainNum);
int answer = n - lostNum.size();
for( int i : lostNum ){
for( int j : reserveNum ){
if( i == j + 1 || i == j - 1 ){
answer++;
reserveNum.remove(j);
break;
}
}
}
return answer;
}
}
5. Map의 활용
○ Key와 Value로 이루어진 구조체 입니다. 이 또한 크게 다른 점은 없기 때문에 간단히만 짚고 넘어가겠습니다. kotlin과 비교할 만 한게 있다면, 참조시에 null을 피하기 위해서라도 .getOrDefault()를 자주 사용하게 된다는 점 입니다.
- 요소의 참조시에 .get(key)
- 만약 해당 key가 없다면 null을 반환하게 된다. 이는 오류가 발생할 수 있으니. 요소의 참조시에 .getOrDefault(key,defaultValue)를 사용하자.
- .merge(key,Value,Lamda) 메서드는 업데이트 시에 유용하다. 메서드 참조나 람다를 같이 이용해보자.
import java.util.Arrays;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Collections;
import java.util.HashMap;
class Main {
public static void main(String args[]) {
HashMap<Integer,String> map = new HashMap<Integer,String>(){{put(0,"딸기");}};
map.put(1,"사과");
map.put(2,"바나나");
map.put(3,"포도");
System.out.println(map);
System.out.println(map.get(1));
System.out.println(map.getOrDefault(5,"없음"));
map.remove(0);
System.out.println(map);
map.clear();
System.out.println(map);
}
}
6. 컬렉션의 공통
○ 위의 컬렉션들(Map 제외)들은 기본적으로 컬렉션을 상속받는 인터페이스 입니다. 따라서 각 컬렉션 마다 보통은 공통적으로 사용하는 메서드나 고려해야할 사항이 있지요. 여기에 대강 적어두었습니다.
- 대부분의 컬렉션은 괄호안에 숫자를 넣으면 크기지정이 된다.
- 반대로 컬렉션(List,Set)등을 넣으면 그 요소가 들어있는 컬력션이 생성된다.
- Map의 경우 컬렉션을 초깃값으로 지정할 수 없다. put을 사용해야 한다.
- 요소의 추가는 약간 다르지만, 다음과 같은 공통이 있다.
- 요소의 참조는 .get()
- 요소의 설정은 .set()
- 요소의 삭제는 .remove()
- 요소의 전체삭제는 .clear()
- 요소의 크기 조회 .size()
- 요소의 추가(List,Set) -> .add() // (Map) -> .put()
- 요소의 여부 확인(List,Set) -> .contains()
- 요소의 정렬(List,Set) -> .sort(null) || Collections.sort() || .stream().sorted()
- 요소 자르기(List,Set) -> .subList(start_Idx,) , subSet(end_Idx)
- Arrays.asList()는 바로 list를 간편하게 생성할 수 있는 키워드이다. 간편하니 외워두자
import java.util.Arrays;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.HashMap;
class Main {
public static void main(String args[]) {
HashSet<Integer> set = new HashSet<>(Arrays.asList(1,2,3)); //Set
ArrayList<Integer> list = new ArrayList<>(set); //Set -> ArrayList
HashMap<String,String> map = new HashMap<String,String>(){{//초기값 지정
put("a","b");}};
System.out.println(set);
System.out.println(list);
System.out.println(map);
}
}
7.정적 컬렉션
○ Mutable 하지 않은, 즉 변경할 수 없는 컬렉션을 의미합니다. 요소를 변경할 수 없지만, 간편하기 때문에 의외로 자주 쓰게 됩니다.
- 기본 컬렉션 (List, Set, Map)의 경우에는 of 메서드로 정적 메서드를 만들 수 있다.
- 기본 컬렉션 (List, Set, Map)외의 컬렉션은 of 메서드사용할 수 없는 듯 하다.
- Map의 경우 .ofEntries() 메서드를 통해서 정적메서드를 만들 수 있다. (import static java.util.Map.*
) 필요 - 정적메서드는 Mutable컬렉션이 아니다. 따라서 Mutable컬렉션으로 사용하려면 컬렉션 생성시, 정적 컬렉션을 생성자의 파라미터로 넣으면 된다. -> ( ex. ArrayList<Integer> arrList = new ArrayList<>(List.of(1,2,3)) )
- 수정 불가 컬렉션도 존재한다. (ex. UnModifiableList) -> 읽기 전용
import java.util.Arrays;
import java.util.List;
import java.util.ArrayList;
import java.util.Set;
import java.util.HashSet;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import static java.util.Map.*;
class Main {
public static void main(String args[]) {
List<Integer> list = List.of(1,2,3);
Set<Integer> set = Set.of(1,2,3);
Map<String, Integer> map = Map.of("a",1,"b",2);
System.out.println(list);
System.out.println(set);
System.out.println(map);
ArrayList<Integer> arrList = new ArrayList<>(list);
HashSet<Integer> hsSet = new HashSet<>(set);
HashMap<String, Integer> hsMap = new HashMap<>(map);
System.out.println(arrList);
System.out.println(hsSet);
System.out.println(hsMap);
Map<String,String> entries = ofEntries(
entry("hello","world"));
System.out.println(entries);
}
}
8. 스트림
스트림은 컬렉션의 연산을 더 유용하게 해 줍니다. 여러가지 특징이 있고 이를 이해만 한다면 매우 유용합니다.
- 스트림은 다음과 같은 특징이 있다.
- 모든 컬렉션은 스트림 메서드를 가지고 있다.
- 종류로는 .stream() .parallelStream() 등이 있다.
- 스트림은 지연방식을 사용한다. 즉 스트림의 마지막 연산이 나올때까지 계속 연산한다. (아마도)
- 컬렉션의 종류와 무관하게 반환은 기본적으로 Stream 클래스이다.
- 스트림은 중간 연산자와 종료연산자(리덕션)으로 나뉘며, 람다함수와 메서드:생성자 참조 사용이 가능하다.
- 중간연산자는 스트림 도중에 연산을 수행한다. (filter(), map(), flatMap() 등..)
- 종료연산자는 스트림의 연산을 끝내고, 특정 형식으로 변환해서 반환한다. (count(), sum(), max() 등)
- 스트림은 다음과 같은 방식으로 생성이 가능하다.
- Stream.of( ... ) - 가변인수 또는 배열을 넣어 만들 수 있다.
- Stream.empty() - 빈 스트림을 만든다.
- Stream.generate( Lamda ) - 무한 스트림을 만든다.
- Arrays.stream( arr, from. to ) - 배열 일부만을 잘라서 만들 수 있다.
import java.util.Arrays;
import java.util.stream.Stream;
class Main {
public static void main(String args[]) {
String[] arr = {"a","b","c","d"};
Stream<Integer> stm1 = Stream.of(1,2,3,4);
Stream<String> stm2 = Arrays.stream( arr, 1, 3 );
Stream<Integer> emptyStm = Stream.empty();
stm1.forEach(System.out::print);
System.out.println("");
stm2.forEach(System.out::print);
System.out.println("");
emptyStm.forEach(System.out::print);
System.out.println("");
}
}
9. 스트림 중간 연산자
○ 스트림 중 중간연산을 할 수 있는 연산자들입니다.
- 스트림의 대표적인 중간 연산자로는 map(), filter(), flatMap() 등이 있다.
- map() - 스트림, 컬렉션의 요소들을 매핑한 스트림을 반환한다.
- filter() - 스트림, 컬렉션의 요소들 중 조건에 맞는 것들의 스트림을 반환한다.
- flatMap() - map()과 비슷하지만, 내부의 요소까지 참조해서 하나의 스트림으로 반환하는 차이점이 있다.
- 컬렉션에서 사용하려면, 컬렉션 내부의 .stream() 메서드를 통해 스트림으로 바꿀 수 있다.
- 중간연산자, 종료연잔자와 혼합하여 사용 가능하다.
import java.util.stream.Stream;
import java.util.List;
class Main {
public static void main(String args[]) {
Stream<Integer> stm = Stream.of(1,2,3,4,5,6,7,8,9,10);
Stream<Integer> stm2 = Stream.of(1,2,3,4,5,6,7,8,9,10);
List<Integer> list = List.of(1,2,3,4,5,6,7,8,9,10);
stm.map(w-> w * 2).forEach(System.out::print);
System.out.println("");
stm2.filter(w-> w > 5).forEach(System.out::print);
System.out.println("");
long num = list.stream().map(w-> w * 2).filter(w-> w > 5).count();
System.out.println(num);
}
}
10. 종료 연산자
○ 리덕션이라고도 합니다. 스트림의 결과를 모으거나 정리해서 반환하는 역활을 합니다. 반환형식은 기본적으로 optional 임에 주의합니다.
1. 리덕션 - 스트림을 다시 바꾸어서 사용할 수 있다.
- 단순 출력 : .forEach(System.out::println)
- 배열로 변환 : .toArray(String[]::new)
- 컬랙션으로 변환 : .collect(Collectors.toList()) // .collect(Collectors.toSet())
- 컬렉션으로 변환(map이 객체를 담고 있을 경우) : .collect(Collectors.toMap(class::key,class::value))
- joinToString의 효과 : .collect(Collectors.joining())
2. 단순 리덕션 - 스트림의 요소 중에서 메서드의 따라 반환형식이나 결과가 다르게 나온다. 대표적으로 다음이 있다.
- .count() : 요소들의 갯수를 반환
- .max() : 요소중의 최댓값을 반환
- .min() : 요소중의 최솟값을 반환
- .sum() : 요소의 총합을 반환
- .average() : 요소의 평균을 반환
- .reduce() : map()처럼 활용하면서 연산을 할 때 사용된다.
- 그 외 : .findFrist(), findAny() 등등 다양한 연산자가 존재한다.
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
class Main {
public static void main(String args[]) {
List<Integer> list = List.of(1,2,3,4);
Set<String> set = Set.of("A","B","C","D");
List<Integer> newList = list.stream().map(i->i+1).collect(Collectors.toList());
Set<Integer> newSet = set.stream().map(i-> i.length()).collect(Collectors.toSet());
System.out.println(newList);
System.out.println(newSet);
int sum = newList.stream().mapToInt(i->i).sum();
long count = newSet.stream().count();
System.out.println(sum);
System.out.println(count);
}
}
11. 스트림을 이용한 형변환
○ 스트림 연산을 유용하게 하려면 요소간의 형변환이 필요하게 될 경우가 있습니다. 아래에는 자주 사용 될 수 있는 것들을 모아봤습니다.
1. 기본 타입 스트림과 map
- 스트림의 요소들은 기본적으로 요소들은 오브젝트이여야 한다.
- 만약 기본 연산자(int, long)는 기본타입 스트림을 사용해야 한다.
- 기본타입 스트림을 사용할 경우 - IntStream, LongStream, DoubleStream등
- 만약 스트림의 요소를 기본타입으로 바꾸려면 map을 통해 바꿀 수 있다. 반환은 각 기본타입 스트림이 된다.
- mapToInt() - [ Stream -> IntStream ]
- mapToLong() - [ Stream -> LongStream ]
- mapToDouble() - [ Stream -> DoubleStream ]
2. map의 활용한 형변환
map()과 참조 메서드, 생성자를 사용하면 스트림상 형변환이 가능하다.
- 기본적으로 클래스 형으로 바꾸고자 한다면 .valueOf() 를 사용한다.
- Integer::valueOf : ( Any -> Integer )
- String::valueOf : ( Any-> String )
- int로 바꾸고자 한다면 Integer의 .parseInt() 를 사용한다.
- Integer::parseInt : ( Any -> int )
3. 스트림을 배열로 변환
- 스트림은 .toArray()메서드를 가지고 있다. 이를 통해 스트림을 배열로 변환 가능하다.
- int를 클래스 형으로 바꿀때에는 생성자를 이용해야 한다. 참조 생성자를 사용하면 편리하다.
import java.util.Arrays;
import java.util.List;
import java.util.ArrayList;
import java.util.stream.Collectors;
class Main {
public static void main(String args[]) {
List<Integer> intList = List.of(1,2,3,4,5,6,7,8,9,10);
List<String> stringList = List.of("1","2","3","4");
String[] StringArr = intList.stream().map(String::valueOf).toArray(String[]::new); // Integer List -> Stirng arr
int[] intArr = stringList.stream().mapToInt(Integer::parseInt).toArray(); // String List -> int arr
System.out.println(Arrays.toString(StringArr));
System.out.println(Arrays.toString(intArr));
List<Integer> newIntList = Arrays.stream(intArr).boxed().collect(Collectors.toList()); // int arr -> Integer List
List<String> newStringList = List.of(StringArr); // Stirng arr -> Stirng List
System.out.println(newIntList);
System.out.println(newStringList);
}
}
참고자료는 다음과 같습니다.
JAVA - ArrayList에서 배열로, 배열에서 ArrayList로
프로그래밍을 하다보면 데이터 자료구조를 변환해야 할 때가 있다. 오늘 포스팅은 ArrayList or List 배열(Array)로 변환하는걸 다루어본다. 중요한건 자료구조를 바꾸는 것이지 자료형은 일치해야 한
mommoo.tistory.com