여러 객체(데이터)를 모아 놓은 것
List, Set, Map은 Collection interface가 타입임
자주 쓰는 Collection 메소드로 add(), remove(), contains(), size(), iterator()가 있음
컴파일시 타입을 체크해 주는 기능
타입 체크와 형변환을 생략해줄 수 있음
static멤버에는 타입 변수 T를 사용할 수 없음
제네릭 타입의 배열 T[ ]를 생성하는 것은 허용되지 않음
ArrayList<String> arr1 = new ArrayList();
List의 Interface에는 ArrayList, LinkedList, Stack , Vector가 있음
순서가 있는 데이터 집합, 중복을 허용함
읽기 속도는 ArrayList가 추가/삭제는 LinkedList 빠름
장점 : toString이 이미 재정의 됨, 객체 출력시 바로 나옴 (주소가 나오지 않음)
ArrayList에서는 중간에 index의 value가 비는 경우가 없
음, 배열과 다름
초기 몇개의 데이터를 사용해야 하는지 반드시 지정할 필요가 없음
ArrayList의 크기는 동적으로 변한다.
Iterator<String> iter = set1.iterator();
while (iter.hasNext()) {
System.out.println(iter.next());
}
put(), get(key), remove(), values(), keySet(), entrySet() 등이 있음
// hm1 = ("one, 1")
for(Entry<String, Integer> entry : hm1.entrySet()) {
System.out.println(entry.getValue());
if("one".equals(entry.getKey())) {
System.out.println(hm1.get(entry.getKey()));
}
}
Map<String, Student> hm2 = new HashMap<String, Student>();
hm2.put("k1", new Student("java", 1, "Junior"));
// arr3 = {1,2,3,4,5}
HashMap<String, ArrayList<String>> hm3 = new HashMap<>();
hm3.put("arr3", arr3);
// arr3 = {1,2,3,4,5}
HashMap<String, HashMap<String, ArrayList<String>>> hm4 = new HashMap<>();
hm4.put("hm3", hm3);
// b값 출력 : 객체이므로 ArrayList로 형변환을 해야함
System.out.println( ((ArrayList)hm4.get("hm3").get("arr3")).get(1) );
// 위와 다르게 제네릭에서 데이터 타입을 지정하면 형변환을 안해도 된다.
System.out.println(hm4.get("hm3").get("arr3").get(1));
}
public static void main(String[] args) {
Stack<String> card = new Stack<String>();
// push() : 저장
card.push("Lotte");
card.push("Hana");
// peek() : 반환
System.out.println(card.peek()); // Hana
System.out.println(card); // Lotte, Hana
// pop() : 반환 + 삭제
System.out.println(card.pop()); // Hana
System.out.println(card); // Lotte
인터페이스 이므로 객체 생성 안됨
Queue<String> drinkBox = new LinkedList<String>();
// add() : 저장
drinkBox.add("Coke");
drinkBox.add("Sprite");
drinkBox.add("PepsiZero");
// peek() : 반환
System.out.println(drinkBox.peek());
System.out.println(drinkBox);
// poll() : 반환 + 삭제
System.out.println(drinkBox.poll());
System.out.println(drinkBox);
Ex) Color
public enum Color { // Color 라고 하는 enum타입의 객체
// 파라미터를 이용하려면 사용자 정의 생성자를 만들어야 함
RED("c01"),
BLUE("c02"),
GREEN("c03");
private String colorCode; // 캡슐화
Color() {};
Color (String colorCode) {
this.colorCode = colorCode;
};
public String getColorCode() {
return this.colorCode;
}
}
Ex) Color Test
package step01.enumtype;
public class ColorTest {
public static void main(String[] args) {
for(Color color : Color.values()) {
System.out.println(color.getColorCode());
} // c01, c02, c03
}
}
람다식 : 함수(메서드)를 간단한 식으로 표현하는 방법
함수와 메서드의 차이
// 추상 메소드가 1개 만 있어서 함수형 인터페이스로 사용할 수 있음
public interface Astermark {
public void addAstermark(String str1, String str2);
}
public class AstermarkImpl implements Astermark{
@Override
public void addAstermark(String str1, String str2) {
System.out.println(str1 + "*" + str2);
}
}
public class AstermarkTest{
public static void main(String[] args) {
// 두 문장을 * 를 기점으로 합쳐서 출력하는 기능
String str1 = "Hello";
String str2 = "Java";
// interface + class
AstermarkImpl astermarkImpl = new AstermarkImpl();
astermarkImpl.addAstermark(str1, str2);
// lambda
Astermark astermark =
(String s1,String s2) -> System.out.println(s1 + "*" + s2);
// 람다식은 익명의 객체이므로 세미콜론 필수!
astermark.addAstermark(str1, str2);
}
}
public class LambdaTest {
// 람다 1, lombok을 이용하여 annotation
@FunctionalInterface
interface Lambda1 {
void method1();
}
// 람다 2
@FunctionalInterface
interface Lambda2 {
void method2(int i);
}
// 람다 3
@FunctionalInterface
interface Lambda3 {
int method3(int x, int y);
}
// * 적용 (심화 계산기)
@FunctionalInterface
interface Calculation {
int operation(int x, int y);
}
static int operate(int x, int y, Calculation calculation) {
return calculation.operation(x, y);
}
public static void main(String[] args) {
Calculation add = (x, y) -> x + y;
Calculation sub = (x, y) -> x - y;
System.out.println(operate(50, 13, add));
System.out.println(operate(50, 13, sub));
// 람다 1 : 매개 변수가 없는 람다식
// 인터페이스에서 람다식 함수(객체) 만들기
Lambda1 lambda1;
// 기본적인 람다식, 객체이므로 코드블럭 뒤에 ;
lambda1 = () -> {
System.out.println("람다 1");
};
//
// 코드 블럭이 없는 람다식
lambda1 = () -> System.out.println("람다 1");
System.out.println(lambda1);
// 람다 2 : 매개 변수가 있는 람다식, 1개(자료형, 코드블럭 생략 가능)
Lambda2 lambda2;
lambda2 = (int i) -> {
System.out.println(i + 10);;
};
// 매개 변수의 자료형과 코드 블럭이 없는 람다식
lambda2 = i -> System.out.println(i + 10);;
lambda2.method2(1);
// 람다 3 : 매개 변수가 2개 이상
Lambda3 lambda3;
lambda3 = (int x, int y) -> {
return x + y;
};
// 자료형, return + 코드 블럭 생략 가능
lambda3 = (x, y) -> x + y;
// 리턴을 사용하면 sysout을 해야 함
System.out.println(lambda3.method3(3, 5));
}
}
다양한 데이터 소스를 표준화된 방법으로 다루기 위한 것
중간 연산과 최종 연산으로 나뉨
스트림은 Iterator처럼 일회용임(필요하면 다시 스트림을 생성해야 함)
최종 연산 전까지 중간연산이 수행되지 않음
// package, import 는 생략
public class StreamTest {
public static void main(String[] args) {
// Optional : null을 처리할 수 있음
// empty() : 빈 Optional 객체 생성
Optional<String> opt1 = Optional.empty();
System.out.println(opt1); // Optional.empty
// of(value / null) : null 이 아닌 데이터 생성 / *NPE 발생
// Optional 안에 null
Optional<String> opt2 = Optional.of(null);
// System.out.println(opt2); // NPE 발생
// ofNullable(value / null) : null 일 수도 있는 값이 들어가는 경우
Optional<String> opt3 = Optional.ofNullable(null);
Optional<String> opt4 = Optional.ofNullable("java");
// null 이 들어가게 되면 Optional 빈 객체 생성
System.out.println(opt3); // Optional.empty
System.out.println(opt4); // Optional[java]
// ifPresent() : Optional 객체 내부에 데이터가 존재하는 경우만 해당 결과값을 반환
opt3.ifPresent(v -> System.out.println(v)); // null이라 실행 안함
opt4.ifPresent(v -> System.out.println(v)); // java
// orElse : 빈 Optional 이라면 orElse 내부 파라미터로 받은 값이 반환
// 값을 내부에 갖고 있는 Optional 이라면 해당 보유 값을 반환
System.out.println(opt3.orElse("orElse")); // orElse
System.out.println(opt4.orElse("orElse")); // java
// orElseThrow : 파라미터로 Exception 객체 전달, 만약 null 아닐경우 해당 보유 객체값을 반환
try {
System.out.println(opt3.orElseThrow(NoSuchElementException::new));
} catch (Exception e) {
// e.printStackTrace(); // NoSuchElementException 발생
}
// orElseGet
// System.out.println(opt3.orElseGet(null)); // NPE 발생
// 스트림 Stream
// step01 : 스트림 생성, 컬렉션, 길이가 있어야 함
ArrayList<Integer> list1 = new ArrayList<Integer>(Arrays.asList(1, 2, 3));
System.out.println(list1);
Stream<Integer> stream1 = list1.stream();
stream1.forEach( num -> System.out.println(num) );
// 배열을 이용한 스트림
int[] intArray = {1, 2, 3};
IntStream intStream = Arrays.stream(intArray);
System.out.println(intStream);
// step02 : 중개 연산
// filter() : 스트림 내 요소중 조건애 맞는 것을 필터 -> 반환
List<String> fruitList = Arrays.asList("mango", "watermelon", "kiwi");
// "m" 문자열이 포함되어 있는 것만 출력
fruitList.stream()
.filter(fruit -> fruit.contains("m"))
.forEach(fruit -> System.out.println(fruit));
// map() : 스트림 내 요소들이 특정 로직 수행 한 후, 새로운 스트림을 반환
List<Integer> integerList = Arrays.asList(2, 1, 3);
integerList.stream()
.map(i -> i + 10)
.forEach(i -> System.out.println(i));
// sorted() : 정렬
integerList.stream()
.sorted()
.forEach(v -> System.out.println(v));
// step03 : 최종 연산
// count, min, max, sum
// collect : toList, toSet, toMap
List<Integer> integerList2 = Arrays.asList(1, 3, 5, 2, 1, 5, 26, 3, 2, 1);
System.out.println(integerList2.stream()
.count());
System.out.println(integerList2.stream()
.collect(Collectors.toSet()));
// 중계 연산
Stream<Integer> numbers = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
//reduce : 누적된 값을 계산
System.out.println(numbers.reduce((x, y) -> x + y ));
// x에 누적값이 오고 y에 다음 차례 값이 대입됨
// forEach : 요소 출력
System.out.println();
Arrays.asList(1, 2, 3).stream()
.forEach(System.out::println);
// map : 지정 연산 이후 반환
System.out.println();
Arrays.asList(1, 2, 3).stream()
.map(v -> v * v)
.forEach(System.out::println);
// skip : 이전 요소 잘라냄
System.out.println();
Arrays.asList(1, 2, 3).stream()
.skip(2) // n : 스킵할 원소 갯수
.forEach(System.out::println);
// limit : 이후 요소 잘라냄
System.out.println();
Arrays.asList(1, 2, 3).stream()
.limit(2) // n : 출력할 원소 갯수
.forEach(System.out::println);
// filter : 1~10까지의 자연수중 홀수만 출력
System.out.println(); //asList로 배열부터 만듦
Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
.stream()
.filter(i -> i % 2 != 0)
.forEach(System.out::println);
// sorted : 기본적으로 오름차순
System.out.println();
Arrays.asList(3, 1, 2)
.stream()
.sorted()
.forEach(System.out::println);
// 내림차순
System.out.println();
Arrays.asList(3, 1, 2)
.stream()
.sorted(Comparator.reverseOrder())
.forEach(System.out::println);
// distinct : 중복된 값은 삭제
System.out.println();
Arrays.asList(3, 1, 2, 4, 2, 5, 1, 1)
.stream()
.distinct()
.forEach(System.out::println);
// 최종 연산
//count, sum // min, max, average : Optinal이라 get으로 가져와야 함
// range로 100개 값 배열로 만들기
System.out.println();
int[] numList = IntStream.range(0, 100).toArray();
System.out.println(numList.length);
long sum22;
sum22 = IntStream.of(1, 2, 3, 4, 12).count();
System.out.println(sum22);
OptionalInt min;
min = IntStream.of(1, 2, 3, 4, 12).min();
System.out.println(min.getAsInt());
// reduce
OptionalInt red;
red = IntStream.range(0, 100).reduce((x, y) -> x + y);
System.out.println(red.getAsInt());
}
}
List와 ArrayList 를 혼동하여 예외를 발생 시킴
asList, List, ArrayList, [ ] 가 헷갈림
Stream의 메소드는 쓰임이 다양하므로 꾸준한 사용으로 익혀야 함
개발일지를 쓰면서 내용이 많을 뿐더러 짧은 요약보다 코드 블럭 자체를 올리면서 다시 복기하는 방법이 좋은 것 같다.
map, filter 처럼 JavaScript에서 사용해본 것이 나와서 이번 파트는 이해가 빨라서 좋았다. 프로그래밍언어는 어느 정도 공통점이 있는 것 같다.