병렬 처리란 멀티코어 환경에서 하나의 작업을 분할해 각각의 코어가 병렬적으로 처리하는 것이다. 자바 8 부터는 병렬 스트림과 포크/조인 프레임워크를 사용하면 쉽게 병렬처리가 가능하다.
공통점은 둘 다 동작방식이 멀티 스레드라는 점이지만 목적은 다르다.
- 동시성
멀티 작업을 위해 멀티 스레드가 번갈아가면서 실행한다.
- 병렬성
병렬성은 멀티 작업을 하기 위해 멀티 코어를 이용해 동시에 실행하는 성질이다.
병렬 스트림은 각각의 스레드에서 처리할 수 있도록 스트림 요소를 여러 청크로 분할한 스트림이다. 따라서 병렬 스트림을 이용하면 멀티코어 프로세서가 각각의 청크를 처리하도록 할당이 가능해진다.
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8);
Integer result = list.parallelStream().reduce(0,Integer::sum);
System.out.println("result = " + result); // <- 36 출력
병렬 스트림은 각각의 스레드에서 처리할 수 있도록 스트림 요소를 여러 청크로 분할한 스트림이다. 때문에 병렬 스트림을 이용하면 멀티 코어 프로세서가 각각의 청크를 처리하도록 할당이 가능해진다. parallelStream()을 호출하면 컬렉션에서 바로 병렬 스트림을 리턴한다.
순차 스트림에 parallel()메소드를 호출하면 기존 스트림을 병렬로 변경이 가능하고, 병렬 스트림에 sequential()메소드를 호출하면 순차 스트림으로 변경할 수 있다.
이 메소드를 호출하면 내부적으로 병렬로 수행해야 함을 의미하는 불리언(boolean) 플래그가 설정되게 된다. 메소드를 통해 연산 마다 병렬 실행과 순차 실행을 수행할 지 제어할 수 있게 된다.
stream.parallel() // <-병렬 스트림으로
.map()
.sequential() // <-순차 스트림으로
.reduce();
mapToInt() : 스트림을 IntStream으로 변환해주는 메서드다.
public static void main(String[] args) {
//sturdent : 이름 / 점수
List<Student> list = Arrays.asList(
new Student("홍길동",82),
new Student("김남준",90),
new Student("주몽",100),
new Student("박혁거세",80),
new Student("온조",70)
);
int max = list.stream()
.mapToInt(Student::getAge)
.max().getAsInt();
System.out.println("max = " + max);
double average = list.stream()
.mapToInt(Student::getAge)
.average().getAsDouble();
System.out.println("average = " + average);
}
max = 100
average = 84.4
🌈tip
Collectors.joining("/") : "/" 로 나뉘어서 출력해준다. 여기서 강력한 기능은 마지막은 "/"가 출력이 안된다. 매개변수에 다른 기호나 문자를 넣어주면 그에 맞게 나뉘어서 출력된다.
String[] stringArray = {"나", "아빠", "엄마"};
Stream<String> nameStream = Arrays.stream(stringArray);
String collect = nameStream.collect(Collectors.joining("/"));
System.out.println("collect = " + collect);
int[] intArray = {1, 2, 3, 4, 5};
IntStream intStream = Arrays.stream(intArray);
intStream.forEach(a -> System.out.print(a + ","));
나/아빠/엄마 <- 이 부분을 잘보면 마지막엔 Collectors.joining("/")로 마지막에 "/" 가 없다.
1,2,3,4,5,
sorted()를 사용할 때 주의점
- 객체 요소일 경우에는 Comparable을 구현하지 않으면 첫번째 sorted() 메소드를 호출하면 ClassCastException이 발생
- 객체 요소가 Compatable을 구현하지 않았거나, 구현 했다하더라도 다른 비교 방법으로 정렬하려면 Comparator를 매개값으로 갖는 두번째 sorted() 메소드를 사용해야 한다.
Comparator 인터페이스
Comparator는 함수적 인터페이스이므로 다음과 같이 람다식으로 매개값을 작성할 수 있다.
sorted((a, b) -? { ... })
- 중괄호 { } 안에는 a와 b를 비교해서 a가 작으면 음수, 같으면 0, a가 크면 양수를 리턴하는 코드를 작성
sorted(); // 1
sorted((a, b) -> a.compareTo(b)); // 2
sorted(Comparator.naturalOrder()); // 3
sorted((a, b) -> b.compareTo(a)); // 1
sorted(Comparator.reverseOrder()); // 2
compareTo메서드를 재정의해준 Student클래스
@Getter @AllArgsConstructor
public class Student implements Comparable<Student>{
private String name;
private Integer score;
@Override
public int compareTo(Student o) {
return Integer.compare(score, o.score);;
}
}
위(오름차순) / 아래(내림차순)
public static void main(String[] args) {
List<Student> studentList = Arrays.asList(
new Student("손흥민", 90),
new Student("박지성", 88),
new Student("차범근", 92)
);
studentList.stream()
.sorted()
.forEach(student ->{
String name = student.getName();
int score = student.getScore();
System.out.println("이름 : " + name + " / 점수 : " + score);
});
System.out.println("----위(오름차순) / 아래(내림차순)----");
studentList.stream()
.sorted(Comparator.reverseOrder())
.forEach(student ->{
String name = student.getName();
int score = student.getScore();
System.out.println("이름 : " + name + " / 점수 : " + score);
});
}
이름 : 박지성 / 점수 : 88
이름 : 손흥민 / 점수 : 90
이름 : 차범근 / 점수 : 92
----위(오름차순) / 아래(내림차순)----
이름 : 차범근 / 점수 : 92
이름 : 손흥민 / 점수 : 90
이름 : 박지성 / 점수 : 88
반환 타입은 Boolean타입(true/false)
- allMatch() :모든 요소들이 매개값으로 주어진 Predicate의 조건을 만족하는지 조사
- anyMatch() : 최소한 한 개의 요소가 매개값으로 주어진 Predicate의 조건을 만족하는지 조사
- noneMatch() : 모든 요소들이 매개값으로 주어진 Predicate의 조건을 만족하지 않는지 조사
public static void main(String[] args) {
int[] intArray = {2, 4, 6};
boolean result = Arrays.stream(intArray).allMatch(a -> a % 2 == 0);
System.out.println("모두 2의 배수인가? " + result);
result = Arrays.stream(intArray).anyMatch(a -> a % 3 == 0);
System.out.println("3의 배수가 있는가? " + result);
result = Arrays.stream(intArray).noneMatch(a -> a % 3 == 0);
System.out.println("3의 배수가 없는가? " + result);
}
모두 2의 배수인가? true
3의 배수가 있는가? true
3의 배수가 없는가? false
최종 처리 기능으로 요소들을 수집 or 그룹핑하는 기능이 있다.
- 필터링 또는 매핑된 요소들로 구성된 새로운 컬렉션을 생성한다.
- 요소들을 그룹핑하고, 집계를 할 수 있다.
@Data
@AllArgsConstructor
public class Student implements Comparable<Student>{
private String name;
private Integer score;
private Sex sex;
@Override
public int compareTo(Student o) {
return Integer.compare(score, o.score);
}
//성별 enum
public enum Sex{
MALE,FEMALE
}
}
public static void main(String[] args) {
List<Student> totalList = Arrays.asList(
new Student("홍길동", 10, Student.Sex.MALE),
new Student("홍길순", 12, Student.Sex.FEMALE),
new Student("김남", 10, Student.Sex.MALE),
new Student("김여", 8, Student.Sex.FEMALE),
new Student("김여", 8, Student.Sex.FEMALE)
);
List<Student> maleList = totalList.stream()
.filter(s -> s.getSex() == Student.Sex.MALE)
.collect(Collectors.toList());
maleList.forEach(s -> System.out.println(s.getName()));
System.out.println("---------------위(남자)/아래(여자)---------------");
Set<Student> femaleSet = totalList.stream()
.filter(s -> s.getSex() == Student.Sex.FEMALE)
.collect(Collectors.toCollection(HashSet::new));
femaleSet.forEach(s -> System.out.println(s.getName()));
}
}
홍길동
김남
---------------위(남자)/아래(여자)---------------
홍길순
김여
- 2가지 방법으로 HashSet으로 수집가능하다..
//1 .collect(Collectors.toCollection(HashSet::new)); //2 .collect(Collectors.toSet());
//List나 Map이 아닌 컬렉션으로의 변환
Stream<String> stream3 = Stream.of("1", "2", "3", "4", "5");
ArrayList<String> streamToArrayList =
stream3.collect(Collectors.toCollection(() -> new ArrayList<>()));
System.out.println("streamToArrayList = " + streamToArrayList);