List에 값을 채운 상태로 생성하는 방법
List<String> list = Arrays.asList("홍길동","전우치","손오공","멀린");
for(String s:list) {
System.out.print(s+"\t");
}
System.out.println();
list.add("aaa"); // 에러발생 → 불변자료
추가 가능하게 하려면 아래와 같이 해준다.
list = new ArrayList<>(list);
list.add("aaa");
for(String s:list) {
System.out.print(s+"\t");
}
Collection의 정렬 기능 활용
문자열을 정렬할 때 기본적으로 오름차순으로 설정
Java의 Collections.sort() 메서드는 내부적으로 TimSort 알고리즘을 사용
TimSort는 Merge Sort와 Insertion Sort를 혼합한 정렬 알고리즘
최악의 시간 복잡도: O(n log n)
최선의 시간 복잡도: O(n) (이미 정렬된 데이터일 때)
Collections.sort(list);
System.out.println();
for(String s:list) {
System.out.print(s+"\t");
}
결과

list자료의 요소로 쓰이는 객체에 클래스를 정의할 때 Comparable 인터페이스를 정의해야한다.
Collections.class
public static <T extends Comparable<? super T>> void sort(List<T> list) {
list.sort(null);
}
정의하려고 보니 Override가 Comparable이 아니라 compare다.
package ex0416;
import java.util.Comparator;
public class StringDesc implements Comparator<String>{
@Override
public int compare(String o1, String o2) {
return o2.compareTo(o1);
}
}
2번째 테스트
package ex0416;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class CollectionSortTest2 {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("홍길동");
list.add("전우치");
list.add("손오공");
Collections.sort(list);
System.out.println(list);
StringDesc cmp = new StringDesc();
Collections.sort(list, cmp);
System.out.println(list);
}
}
결과
[손오공, 전우치, 홍길동]
[홍길동, 전우치, 손오공]
→ 원본이 바뀐 것은 아님, 대입연산X
[참고] 내림차순 정렬
Collections.reverse(list);
System.out.println();
for(String s:list) {
System.out.print(s+"\t");
}
@AllArgsConstructor
@Getter
@ToString
→ @Data를 쓰지 않은 이유: 데이터 생성 시 초기값을 두고 변경하지 않겠다.
package ex0416;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.ToString;
@AllArgsConstructor
@Getter
@ToString
public class Person {
private String name;
private int age;
}
package ex0416;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class PersonSortTest {
public static void main(String[] args) {
Person p1 = new Person("aaa",20);
Person p2 = new Person("ccc",20);
Person p3 = new Person("bbb",20);
Person p4 = new Person("zzz",20);
Person p5 = new Person("ttt",20);
List<Person> list = new ArrayList<>();
list.add(p1);
list.add(p2);
list.add(p3);
list.add(p4);
list.add(p5);
System.out.println(list);
Collections.sort(list); // Person class 에 comparable을 만들지 않아서 어떤 기준으로 정렬해야할지 모름
System.out.println(list);
}
}
Person.java 수정해주고
package ex0416;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.ToString;
@AllArgsConstructor
@Getter
@ToString
public class Person implements Comparable<Person>{
private String name;
private int age;
@Override
public int compareTo(Person o) {
return this.name.compareTo(o.name); // 오름차순
// return o.name.compareTo(this.name); // 내림차순
}
}
다시 테스트하면 에러가 사라지고 아래와 같이 결과가 나온다.
[Person(name=aaa, age=20), Person(name=ccc, age=20), Person(name=bbb, age=20), Person(name=zzz, age=20), Person(name=ttt, age=20)]
[Person(name=aaa, age=20), Person(name=bbb, age=20), Person(name=ccc, age=20), Person(name=ttt, age=20), Person(name=zzz, age=20)]
compareTo의 return type은 int인데, compareTo를 한 값을 출력해보니 아래와 같이 나왔다.
-1
1
24
23
17
-6
→ 양수, 음수, 0 중에 나온다.
나이로 정렬하기
public int compareTo(Person o) {
return this.age-o.age;
}
package ex0416;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class CollectionSearchTest {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("홍길동");
list.add("전우치");
list.add("손오공");
Collections.sort(list);
System.out.println(list);
int idx1 = Collections.binarySearch(list, "홍길동");
System.out.println(idx1);
}
}
결과
[손오공, 전우치, 홍길동]
2
없는 데이터를 검색하면 음수값을 반환
반환되는 값을 활용해서 아래와 같이 출력이 가능하다.
if(idx1<0)
System.out.println("해당 데이터는 리스트에 없습니다.");
else
System.out.println("해당 데이터는 리스트의 "+(idx1+1)+"번째에 있습니다.");
package ex0416;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
public class CollectionsCopyTest {
public static void main(String[] args) {
// Inmutable(변경 불가능한) 객체 생성
List<String> src = Arrays.asList("홍길동","전우치","손오공","멀린");
// 수정 가능한 리스트 생성
List<String> dst = new ArrayList<>(src);
System.out.println("1dst: "+dst);
// 정렬 수행
Collections.sort(dst);
System.out.println("2dst: "+dst);
// 다시 정렬 이전 상태로 돌아갈 필요가 생겼다.
Collections.copy(dst, src);
System.out.println("3dst: " + dst);
System.out.println("3srt: " + src);
// 수정가능한 상태인지 확인 (Mutable 객체)
dst.remove(0);
System.out.println("4dst: " + dst);
// 반대로 copy 했을 때는 안된다.
Collections.copy(src, dst);
System.out.println("5dst: " + dst);
System.out.println("5src: " + src);
}
}
내부 클래스가 외부 클래스의 값을 제약없이 사용 가능.
클래스 파일을 1개만 만들 수 있다.(관리 이점)
내부 클래스는 외부 클래스에만 필요한 클래스다.(다른 클래스에는 필요X)
외부 클래스가 내부 클래스를 매우 필요로 할 때 사용.
내부클래스를 멤버변수화
package ex0416;
public class Outer1 {
private int speed = 10;
// 이너클래스: 클래스 파일 안의 클래스
class MemberInner1{
public void move() {
System.out.printf("인간형 유닛이 %d 속도로 이동합니다.\n", speed);
// 내부 클래스가 감싸는 클래스의 값을 사용
}
}
public void getUnit() {
MemberInner1 inner = new MemberInner1();
inner.move();
}
}
package ex0416;
public class MemberInnerTest {
public static void main(String[] args) {
// 외부 클래스에서 직접 작동
Outer1 out = new Outer1();
out.getUnit();
// 다른 곳에서 내부 클래스를 생성 가능(외부를 거쳐서 만들어야 함)
Outer1.MemberInner1 inner = out.new MemberInner1();
inner.move();
}
}
결과
인간형 유닛이 10 속도로 이동합니다.
인간형 유닛이 10 속도로 이동합니다.
package ex0416;
public class HumanCamp {
private int speed = 10;
public void getMarine() {
class Marine{
public void move() {
System.out.println("인간형 유닛이 "+speed+" 속도로 이동합니다.");
}
}
Marine inner = new Marine();
inner.move();
}
}
package ex0416;
public class LocalInnerTest {
public static void main(String[] args) {
HumanCamp hc = new HumanCamp();
hc.getMarine();
}
}
결과: 인간형 유닛이 10 속도로 이동합니다.
예제1과 2의 차이점: 클래스가 멤버처럼 있는지, 멤버함수 안에 있는지
멤버 → 전역 | 멤버 안 → 지역
예제2: 나만이 사용 가능한 클래스를 생성, 외부클래스의 메서드에서만 내부클래스를 생성 가능
인터페이스 클래스
package ex0416;
public interface Unit {
void move();
}
HumanCamp2 클래스
package ex0416;
public class HumanCamp2 {
private int speed = 10;
public Unit getMarine() {
class Marine2 implements Unit{
@Override
public void move() {
System.out.println("인간형 유닛이 "+speed+" 속도로 이동합니다.");
}
}
return new Marine2();
}
}
메인
package ex0416;
public class AnnoymousInner1Test {
public static void main(String[] args) {
HumanCamp2 hc = new HumanCamp2();
Unit unit = hc.getMarine();
unit.move();
}
}
HumanCamp2 를 아래와 같이 수정 가능하다.
문법적으로 말은 안되는데 가능함.
또 클래스 이름이 사라짐 Marin2 → 익명
package ex0416;
public class HumanCamp2 {
private int speed = 10;
public Unit getMarine() {
return new Unit() {
@Override //추상메서드이니까 완성은 시켜야함
public void move() {
System.out.println("인간형 유닛이 "+speed+" 속도로 이동합니다.");
}
};
}
}
화살표(->)가 붙어있는 표현식
Unit.java
package ex0416;
public interface Unit {
void move();
}
Human.java
package ex0416;
public class Human implements Unit{
@Override
public void move() {
System.out.println("인간이 움직입니다.");
}
}
Main.java
package ex0416;
public class LambdaTest1 {
public static void main(String[] args) {
// 가능한 문법
Unit unit = new Human();
unit.move();
// 익명클래스
Unit unit = new Unit() {
@Override
public void move() {
System.out.println("인간이 움직인다구요..");
}
};
unit.move();
Unit unit =
() -> {
System.out.println("인간이 움직인다구요..");
};
unit.move();
} // End of Main
}
함수 이름이 필요없다고 지운다?
ex) 교실에 여러 명인데 '야 일로와' 했을 때 누구인지 모른다, 만약 한명이면 누구인지 안다.
인터페이스에 메서드가 하나일 경우에 이런 상황에 적용이 가능하다.
이런 문법을 Lambda식이라고 한다.
Unit2
package ex0416;
public interface Unit2 {
void move(String s);
}
Main 일부
Unit2 unit2 = (String s) ->{
System.out.println(s);
};
unit2.move("move move");
Unit2 unit2 = s -> System.out.println(s);
매개변수가 하나일 때 함수 소괄호()를 생략할 수 있다.
매개변수의 타입도 생략할 수 있다.
명령어가 한 줄일 경우 블록{} 도 생략할 수 있다.
소괄호를 사용해야한다.
public interface Unit2 {
void move(String s, int n);
}
Unit2 unit2 = (s,n) -> System.out.println(s+", "+n);
unit2.move("move move",3);
public interface Unit2 {
String move(String s);
}
Unit2 unit2 = s -> {return s+"!";};
System.out.println(unit2.move("hey"));
결과: hey!
public interface Unit2 {
int move(String s);
}
Unit2 unit2 = s -> s.length();
int result = unit2.move("hey");
System.out.println(result);
결과: 3
기능 사용을 위해 클래스, 인스턴스를 생성하는 과정을 없앤 것.
람다식을 사용할 때 메서드가 반드시 하나만 있어야 한다.
람다식으로 사용할 인터페이스는 메서드가 한 개여야 하는데,
또 다른 메서드를 작성하면 오류가 나지 않으므로 그 표시를 FunctionalInterface로 해준다.
package ex0416;
@FunctionalInterface // 메서드가 1개만 있어야한다.
public interface Unit2 {
int move(String s);
//int fly(int x);
}
향상된 for문과 Iterator는 사실상 같음
기본 for문과의 차이점: 기본 for문은 반복을 제어할 수 있지만 향상된 for문과 Iterator는 처음부터 끝까지 순서대로
package ex0416;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
public class IteratorTest {
public static void main(String[] args) {
List<String> list = Arrays.asList("aaa","bbb","ccc");
// 기본 for문
for(int i=0; i<list.size(); i++) {
System.out.print(list.get(i)+"\t");
}
System.out.println();
// 향상된 for문 → 실제로 컴파일될 때 Iterator로 돌아감
for(String s: list) {
System.out.print(s+"\t");
}
System.out.println();
// 컬렉션에서 제공하는 반복자(list, set)를 쓸 때 사용하는 반복문
Iterator<String> itr = list.iterator();
while(itr.hasNext()) {
System.out.print(itr.next()+"\t");
}
}
}
결과
aaa bbb ccc
aaa bbb ccc
aaa bbb ccc
데이터의 흐름 or 흐르는 통로
기존 자료를 변경하지 않는다.
중간연산과 최종연산으로 구성된다.
한 번 생성하고 사용한 Stream은 재사용할 수 없다. int n = stm2.sum()을 하면 오류
package ex0416;
import java.util.Arrays;
import java.util.stream.IntStream;
public class Steam1 {
public static void main(String[] args) {
int[] arr = {1,2,3,4,5};
// 1. 스트림 생성
IntStream stm1 = Arrays.stream(arr);
// 2. 중간 연산
IntStream stm2 = stm1.filter(n -> n%2==1);
// 3. 최종 연산
int num = stm2.sum();
System.out.println(num);
}
}
아래는 파이프라인, 메서드체이닝이라고도 부르는 방법
int num2 = Arrays.stream(arr)
.filter(n -> n%2==0)
.sum();
System.out.println(num2);
한 줄로 표현도 가능
System.out.println(Arrays.stream(arr).filter(n->n%2==1).sum());
package ex0416;
import java.util.Arrays;
import java.util.List;
public class StreamSortedTest {
public static void main(String[] args) {
List<String> list = Arrays.asList("홍길동","해리포터","멸린");
list.stream().sorted().forEach(n -> System.out.println(n+"\t"));
}
}
Stream 구문을 보면 아래와 같은 역할을 한다.
list.stream() //스트림생성
.sorted() //정렬(중간연산)
.forEach(n -> System.out.println(n+"\t")); // 하나씩 꺼내서 소비함(최종연산)
결과(가나다 순 정렬)
멸린
해리포터
홍길동
문자열의 길이를 기준으로 정렬
// 오름차순
list.stream()
.sorted( (s1, s2) -> s1.length() - s2.length())
.forEach(n-> System.out.println(n+"\t"));
// 내림차순
list.stream()
.sorted( (s1, s2) -> s2.length() - s1.length())
.forEach(n-> System.out.println(n+"\t"));
- List list = Arrays.asList(~~); // 불변의 리스트
- Collection
- sort: 정렬 → compare를 반드시 구현해줘야 함
- search: 몇번째 인덱스에 있는지?
- copy: 복사
원본이 바뀐다.
3.Outer/Inner Class
나만 사용하는 클래스
클래스 1개에 여러 개 클래스 관리 가능
[선언방법]
1. 멤버변수 구역 선언: 외부클래스.내부클래스로 접근 가능
2. 멤버함수 안에 선언: 접근 어려움 숨김
- 익명 클래스로 됨
클래스 선언할 때 생성자, 인스턴스 생성해야하지만,
Lambda식을 사용해서 그 과정을 생략할 수 있게 함
매개변수 타입 생략 가능
매개변수가 하나일 때 소괄호 생략가능
명령어가 한 줄일 경우 블록 생략가능
인터페이스 클래스에서 메소드가 한 개일 때
@FunctionalInterface로 지정해주면 추가 메소드를 쓰는 것을 방지
- 반복문 형태
- 기본 for문: 제어가 가능함
for(int i=0; i<x; i++)- 향상된 for문 → 컴파일과정에서 Iterator함
for(String s : list)- Iterator
Iterator itr = list.iterator();
while(itr.hasNest()) { }
- Stream
데이터의 흐름, 통로
Stream에서 수행하는 연산은 기존 자료를 변경하지 않는다.
중간, 최종연산으로 구성, 일회성