Develop

[JAVA] 배열(Array)과 스트림(Stream) 본문

웹 개발/Java

[JAVA] 배열(Array)과 스트림(Stream)

개발 기록 2024. 1. 19. 17:28

배열(Array)

자바에서 배열을 선언하는 방법

// 값 바로 넣기
String[] arr = {배열에 들어갈 값};
int[] arr = {배열에 들어갈 값};

ex)
int[] arr = {1,2,3};

// 크기만 지정
String arr[] = new String[배열크기];
int arr[] = new String[배열크기];

ex)
int arr[] = new int[5];

 

 

※ java.util.Arrays 를 import하는 것 잊지말기(보통은 자동으로 됨)

 

java.util 패키지

java.util 패키지에는 프로그램을 개발하는 데 사용할 수 있는 유용한 유틸리티 클래스가 다수 포함되어 있다.

실제로 java.lang 패키지 다음으로 가장 많이 사용되는 패키지가 java.util 패키지이다.

하지만 import 문을 사용하지 않아도 바로 사용할 수 있는 java.lang 패키지와는 달리 java.util 패키지는 import 문으로 패키지를 불러오고 나서야 클래스 이름만으로 사용할 수 있다.

java.util.Arrays 클래스

Arrays 클래스에는 배열을 다루기 위한 다양한 메소드가 포함되어 있다.

Arrays 클래스의 모든 메소드는 클래스 메소드(static method)이므로, 객체를 생성하지 않고도 바로 사용할 수 있다.

이 클래스는 java.util 패키지에 포함되므로, 반드시 import 문으로 java.util 패키지를 불러오고 나서 사용해야 한다.


스트림(Stream) (자바 1.8부터 지원됨)

스트림이란 자료의 대상과 관계없이 동일한 연산을 수행해야 할 때 배열, 컬렉션을 대상으로 연산을 수행한다

일관성 있는 연산으로 자료의 처리를 쉽고 간단하게 한다

자료 처리에 대한 추상화가 구현되었다고 한다

즉, collection, array 등의 요소들을 하나씩 참조해서 람다식이라고 하는 함수형 인터페이스를 통해 원하는 방식으로 반복적인 처리가 가능하다

 

자료에 대한 스트림을 생성하여 연산을 수행하면 스트림은 소모되고 재사용 할 수 없다.

스트림 연산은 메모리 공간을 별도로 할당하여 진행되기 때문 기존 자료를 변경하지 않는다.

즉, Stream연산의 경우 자료에는 영향을 주지 않지만 한번 사용하고 없어지는 연산이기 때문에 사용할 때마다 매번 Arrays.stream(변수명)을 해줄 필요가 있다.

 

스트림 연산은 중간 연산과 최종 연산으로 구분되며 중간 연산은 여러 개의 연산이 적용될 수 있지만, 최종 연산은 마지막에 한번만 적용된다.

최종 연산이 이루어져야 결과를 알 수 있으며, 중간 연산의 결과는 알 수 없다. 이를 지연연산이라고 한다.

 

stream의

 

장점

1. 사용하기 편하다

2. 코드가 간결하다

3. 가독성이 높다

 

단점

1. 디버깅이 힘들다

2. 재활용이 불가능하다

3. 속도가 느리다

 

 

ex1) .sum() 과 .count() 예시

int[] arr = {1,2,3};

int sum = Arrays.stream(arr).sum(); // 배열의 합을 구하는 연산

System.out.println(sum); // 6

​

int len = (int)Arrays.stream(arr).count(); // 배열의 갯수를 세어보는 연산

System.out.println(len); // 3

 

 

ex2) int타입의 배열값을 중복 제거 후 내림차순으로 정렬해서 list로 반환하라

Arrays.stream(arr).boxed()	// stream 생성(선언)
                .distinct()	// 중복 제거(가공)
                .sorted(Comparator.reverseOrder())	// 내림차순 정렬(가공)
                .collect(Collectors.toList())	// list로 반환(반환)

 

이렇게 stream은 생성(선언), 가공, 반환 세 부분으로 이루어진다


1. 생성(선언) : 배열, 컬렉션(list, set, map 등) 등을 stream 형태로 만든다

String[] arr = {"a", "b", "c", "a", "b", "c", "f"};
List<String> list = Arrays.asList(arr); 

// 배열 stream 생성
Stream<String> stream = Arrays.stream(arr);
// 컬렉션 stream 생성
Stream<String> cStream = list.stream();
// 병렬처리 stream 생성
Stream<String> paStream = list.parallelStream();

 

이렇게 stream을 선언하고 값을 넣는 방법도 있지만

Arrays.stream(arr).메소드()...
list.stream(arr).메소드()...

 

이렇게 직접 바로 넣을 수도 있다


 

2. 가공(stream을 필요한 형태로 가공한다)

 

.boxed()

: int, long, double 등 숫자 타입의 배열을 stream으로 만들 경우 stream 각종 메서드를 사용하기 위해 사용한다

: IntStream 을 Stream으로 변환할 때 boxed()를 사용한다

: int 자체로는 Collection에 못 담아서 Integer 클래스로 변환하여 List<Integer> 로 담는 용도 등에 사용한다

ex) IntStream -> Stream<Integer>

 

주의점! 컬렉션 스트림에서는 이 메서드를 사용하지 않는다고 한다!

 

 

.sorted()

: 정렬할 때 사용

: 안에 Comparator를 파라미터로 지정해서 정렬 방식을 선택할 수 있다

// 배열 정렬
// 배열 내림차순 정렬
Arrays.stream(arr).boxed().sorted().collect(Collectors.toList());

// 컬렉션 정렬
// 컬렉션(list)의 크기 확인
list.stream().sorted().collect(Collectors.toList());

 

// 배열 내림차순 정렬(역정렬)
Arrays.stream(arr).boxed().sorted(Comparator.reverseOrder()).collect(Collectors.toList());

// 컬렉션 내림차순 정렬(역정렬)
list.stream().sorted(Comparator.reverseOrder()).collect(Collectors.toList());

 

 

.findFirst()

: 스트림의 첫번째 값을 가져온다

// 배열 스트림의 첫번째 값을 가져온다.
System.out.println(Arrays.stream(arr).findFirst().getAsInt());

// 컬렉션 스트림의 첫번째 값을 가져온다.
System.out.println(list.stream().findFirst().get())

 

 

.skip(index)

: 처음부터 해당 인덱스까지 생략하고 그 다음 인덱스부터 값을 가져온다

// 배열 스트림의 첫번째 인덱스를 제외한 값만 가져온다.
Arrays.stream(arr).boxed().skip(1).collect(Collectors.toList());

// 컬렉션 스트림의 첫번째 인덱스를 제외한 값만 가져온다.
list.stream().skip(1).collect(Collectors.toList());

// 컬렉션 스트림의 첫번째 인덱스를 제외한 값만 가져온다.
// (skip에 0을 넣어보니 변화가 없는 걸 보니 1번지부터 시작인 듯 싶다.)
list.stream().skip(0).collect(Collectors.toList());

 

 

.skip(배열크기 -1).findFirst()

: 스트림의 마지막 인덱스 값 가져오기

// 배열 스트림의 마지막 값 찾기
Arrays.stream(arr).skip(arr.length -1).findFirst().getAsInt();

// 배열 스트림의 마지막에서 두번 째 값 찾기
Arrays.stream(arr).skip(arr.length -2).findFirst().getAsInt();

// 컬렉션 스트림의 마지막 값 찾기
list.stream().skip(list.size() - 1).findFirst().get));

// 컬렉션 스트림의 마지막에서 두번 째 값 찾기
list.stream().skip(list.size() - 2).findFirst().get();

 

 

.limit(index)

:  인덱스까지만 값을 가져온다

: (==) index 수만큼의 값만 가져온다

// 배열 스트림의 1번째부터 5번째 인덱스까지만 값 가져오기
Arrays.stream(arr).boxed().limit(5).collect(Collectors.toList());

// 컬렉션 스트림의 1번째부터 5번째 인덱스까지만 값 가져오기
list.stream().limit(3).collect(Collectors.toList());

 

 

.distinct()

: 중복 생략

// 배열 스트림의 중복 생략
Arrays.stream(arr).boxed().distinct().collect(Collectors.toList());

// 컬렉션 스트림의 중복 생략
list.stream().distinct().collect(Collectors.toList());

 

 

.max(데이터타입::compare) / .min(데이터타입::compare)

: 최대값 / 최소값

// 최대값
// 배열 스트림
Arrays.stream(arr).boxed().max(Integer::compare).get();

// 컬렉션 스트림
list.stream().max(Integer::compare).get();


// 최소값
// 배열 스트림
Arrays.stream(arr).boxed().min(Integer::compare).get();

// 컬렉션 스트림
list.stream().min(Integer::compare).get();

 

 

.average()

: 평균 

// 배열 스트림의 평균
Arrays.stream(arr).average().getAsDouble();

// 컬렉션 스트림의 평균
// 컬렉션의 경우 .mapToDouble(Integer:doubleValue)을 한번 사용 후 .average() 사용 가능
list.stream().mapToDouble(Integer::doubleValue).average().getAsDouble();

 

 

※ max(), min(), average() 는 실제 리턴해보면 OptionalInt(1) 이라고 찍힌다  
우리가 일반적으로 사용하는 int 타입으로 바꾸려면 getAsInt()를 해주어야 한다!

 

 

.sum()

: 합

// 배열 스트림의 합계
Arrays.stream(arr).sum();

// 컬렉션 스트림의 합계
// .average()와 마찬가지로 컬렉션의 경우 mapToInt(Integer::intValue)를 사용후 가능
list.stream().mapToInt(Integer::intValue).sum();

 

 

.reduce()

: 누적계산에 사용된다

: 배열의 요소 a와 b를 각각 받아서 연산을 수행한다

: 결과값만 알려준다

int[] arr = {1, 2, 3, 4, 5};
Arrays.stream(arr).reduce((a, b) -> a+b).getAsInt();

// 결과
// 15

 

 

.filter()

: 조건을 걸어서 해당 조건에 true인 요소만 걸러낸다.

int[] arr = {1, 2, 3, 4, 5};
Arrays.stream(arr).filter(x -> x % 2 == 0).forEach(x -> System.out.println(x));

// 필터는 조건식을 통해 필터링 해주는 메서드니까 위처럼 사용할 수 있다.
// 뒤에 forEach() 는 출력을 통해 원하는 결과가 나왔는지 확인하려고 추가한 코드이다.

// 결과
2
4

 

 

.map()

: 요소마다 연산을 적용할 때 사용한다

List<String> list = Arrays.asList("I", "am", "student");
list.stream().map(x -> x.toUpperCase()).forEach(x -> System.out.println(x));

// 맵을 사용해서 각 요소를 대문자로 변경해서 출력하도록 한 코드이다.

 

+) asList()

int[] arr = {1, 2, 3};
Arrays.stream(arr);

List<Integer> list = Arrays.asList(1, 2, 3);
list.stream();

Stream<Integer> st = stream.of(1, 2, 3);

 

stream은 위처럼 배열에서 생성할 수도 있고 리스트에서 생성할 수도 있고 직접 생성도 가능하다.

추가적으로 설명하자면 List 에서 Arrays의 메서드를 끌어다 쓴 이유는
리스트는 abstract라서 실제 구현체가 있어야 하기 때문이다
예를 들면 어레이리스트, 링크드리스트 같은 것들.


그런데 지금은 그냥 리스트를 만드려는 것이기 때문에
Arrays의 asList 메서드를 통해 직접초기화를 해준 것이다.

 

 

.mapToInt()

: 예를 들어 값이 문자열로 "2"라고 되어있다면 int타입의 2로 바꾸어준다

: mapToDouble등 매서드는 더 있다

String[] str = {"1", "2"};
Arrays.stream(str).mapToInt(Integer::parseInt).forEach(x -> System.out.println(x));

// 문자열 1과 2를 정수타입으로 바꿔준다.

 

 

.collect()

: 각 요소를 변형하고 원하는 데이터 타입으로 리턴해준다

: 변형은 선택이며 주로 데이터 타입을 변경할  때 사용한다

: 해쉬, 배열, 맵, 리스트 등 원하는 데이터 타입으로 바꿀 수 있으며

파라미터는 달라진다

HashSet<String> fruitHashSet = fruits.collect(HashSet::new, HashSet::add, HashSet::addAll);

 

 

.iterator()

: iterator() 메서드로 이터레이터를 잡아주면 된다
: Iterator<> 타입으로 되어있으며 해당 이터레이터를 통해서 hasNext() next() 의 메서드와 함께 주로 사용한다. 

Iterator<String> iter_list = list.stream().iterator();
while(iter_list.hasNext()){
	System.out.println(iter_list.next());
}

 

 

.noneMatch()

: 매칭되는 값이 없다면 True를 리턴한다.

예시에서는 배열에 2가 있으므로 False를 리턴한다

int[] arr = {1, 2, 3};
Arrays.stream(arr).noneMatch(x -> x == 2);

 

 

.anyMatch()

: 하나라도 매칭되면 True를 리턴한다.

예시에서는 배열에 2가 있으므로 True를 리턴한다

int[] arr = {1, 2, 3};
Arrays.stream(arr).anyMatch(x -> x == 2);

 

 

.allMatch()

: 모든 요소가 다 매칭되야 트루를 리턴합니다.

예시에서는 배열에 1,3이 있으므로 False를 리턴한다

int[] arr = {1, 2, 3};
System.out.println(Arrays.stream(arr).allMatch(x -> x == 2));

 


3. 반환 : 가공한 값을 원하는 형태로 가져오기

 

값이 하나라면 get(), getAsInt()등으로 가져온다

 

배열로 가져오려면 끝에 .toArray() 사용

 

컬렉션 형태는 .collect(Collectors.toList()) 이렇게 가져오면 된다

만약 List가 아닌 다른 걸로 가져오고 싶다면 Collectors.toList() 대신

toSet(), toMap() 를 입력하여 가져온면 된다

 

반환 개수로 반환하기

Long cnt = Arrays.stream(arr).boxed().sorted().collect(Collectors.counting());

 

 

특정 문자열을 구분자로 붙여서 반환하기

Arrays.stream(strArr).collect(Collectors.joining("|"));

 

 

여기까지 출처를 참고하여 이해한 내용을 바탕으로 재구성 및 재정리 하였다

다음번에는 람다식과 Map , 컬렉션에 대해 좀 더 공부해야겠다


 

출처

 

[Java] Java Stream 사용법 정리(jdk 1.8 부터 사용 가능) (tistory.com)

 

[Java] Java Stream 사용법 정리(jdk 1.8 부터 사용 가능)

목차 Java Stream 사용법 정리😃 Java 1.8부터 지원되기 시작한 stream사용법에 대해 기록한다. 그동안 stream에 대해 깊게 공부해본 적이 없어서 이 기회에 제대로 공부해 보도록 하겠다. 앞으로 jdk 1.8

yaga.tistory.com

 

[JAVA] 코테문법총정리1 - 배열과 Arr.. : 네이버블로그 (naver.com)

 

[JAVA] 코테문법총정리1 - 배열과 Arrays, Stream

실제 프로그래밍을 할 때는 아주 가끔 사용하지만, 코딩테스트 문제를 풀 때 만큼은 유독 많이 쓰이는 라이...

blog.naver.com

 

코딩의 시작, TCP School

 

코딩교육 티씨피스쿨

4차산업혁명, 코딩교육, 소프트웨어교육, 코딩기초, SW코딩, 기초코딩부터 자바 파이썬 등

tcpschool.com