게시글
질문&답변
Stream.of(names).forEach(System.out::println) 과 names.stream.forEach(System.out::println) 작동방식의 차이
재현님 답변에 자세한 설명을 더해서 하겠습니다.1. 리턴 타입 확인일단 리턴 타입을 확인하기 위해 names.stream(), Stream.of(names) 코드만 작성하여 Stream 변수에 담아보도록 하겠습니다.import java.util.*; import java.util.stream.Stream; public class Main { public static void main(String[] args) { List names = List.of("Kim", "Lee", "Park", "Kang"); // return: Stream Stream stream1 = names.stream(); // return: Stream> Stream> stream2 = Stream.of(names); // Stream stream2 = Stream.of(names); // type error } }전자의 경우: 질문자님이 예상한 것과 같이 Stream 타입의 변수에 담기지만, 후자의 경우: 타입이 맞지 않아 Incompatible types 컴파일 에러가 발생하고, 그 메시지는 다음과 같습니다.메시지: Incompatible types. Found: 'java.util.stream.Stream>', required: 'java.util.stream.Stream'즉 후자의 경우에는 예상과는 다르게 Stream> 타입이 리턴된다는 것인데요. 이와 관련해서는 names.stream()과 Stream.of(names) 각 메서드 구현에서 설명이 되어있습니다.2. 각 메서드의 구현 확인(설명에 필요한 부분만 일부 잘라서 가져왔습니다.) Collection.stream() 메서드 - 컬렉션 프레임워크 구현체들이 사용할 수 있는 인스턴스 메서드(Collection.java에 위치)/** * Returns a sequential {@code Stream} with this collection as its source. * * @return a sequential {@code Stream} over the elements in this collection * @since 1.8 */ default Stream stream() { return StreamSupport.stream(spliterator(), false); }해당 메서드는 Stream를 리턴합니다.주석을 보면 "a sequential {@code Stream} over the elements in this collection" 라는 부분이 있습니다.컬렉션구현체.stream()으로 호출을 하면 해당 컬렉션구현체의 elements들에 대한 Stream을 리턴한다고 합니다.조금 비약이 있지만, 컬렉션구현체 껍데기는 무시하고, 그 내부의 요소들만 빼내서 Stream으로 만든다는 것입니다. 이 Stream의 타입은 당연히 컬렉션구현체의 타입과 동일하겠죠.List names = List.of("Kim", "Lee", "Park", "Kang");에 대해서names.stream()을 호출하면, List 구현체인 names의 안에 들은 String 타입의 요소들 "Kim", "Lee", "Park", "Kang"만 빼와서 Stream을 생성합니다.따라서 forEach(System.out::println);로 내부 반복을 돌리면 Stream의 각 요소인 String 타입들에 대해서 수행합니다. Stream.of() 메서드 - Stream 클래스의 정적 팩토리 메서드 (Stream.java에 위치)/** * Returns a sequential ordered stream whose elements are the specified values. * * @param the type of stream elements * @param values the elements of the new stream * @return the new stream */ public static Stream of(T... values) { return Arrays.stream(values); }해당 메서드는 T 타입의 values를 인자로 받아서 Stream를 리턴합니다.주석을 보면 다음 문장들이 있습니다.Returns a sequential ordered stream whose elements are the specified values.-> values를 요소로 가지는 Stream을 리턴한다.@param values the elements of the new stream-> 새로운(즉 리턴할) Stream의 요소들인 values를 파라미터로 가진다.이는 Stream.of(values...)과 같이 values를 받아서 values 자체를 Stream의 요소로 사용한다는 것입니다.이는 위의 Collection.stream()에서 [ 컬렉션구현체 껍데기는 무시하고, 그 내부의 요소들만 빼내서 ]라고 언급했던 것과는 정반대입니다.인자로 List 등의 컬렉션구현체가 오거나, 1, 2 등의 숫자값이 오거나, "A", "B" 등의 문자열이 오거나 뭐가 오는지에 관계없이 [ 인자로 온 것 그 자체를 Stream의 요소로 사용하겠다 ]는 것입니다.List names = List.of("Kim", "Lee", "Park", "Kang"); // 따라서 이 경우에는 인자로 온 List 그 자체를 Stream의 요소로 사용합니다. // 따라서 Stream>이 리턴됩니다. // 가 중첩되어서 헷갈린다면 그냥 Stream라고 생각하셔도 되겠습니다. Stream> stream2 = Stream.of(names);따라서 Stream.of(names).forEach(System.out::println); 코드는 Stream의 각 요소 (이 경우에는 List names 하나)에 대해서 콘솔출력을 수행합니다.이는 System.out.println(names);와 동일한 동작을 수행합니다. 이때 모든 컬렉션 프레임워크 구현체들은 toString()이 구현되어 있으므로(아래 참고) 이를 호출합니다.따라서 출력: [Kim, Lee, Park, Kang]모든 컬렉션 프레임워크 구현체가 가지는 toString() - 위치: AbstractCollection.javapublic String toString() { Iterator it = iterator(); if (! it.hasNext()) return "[]"; StringBuilder sb = new StringBuilder(); sb.append('['); for (;;) { E e = it.next(); sb.append(e == this ? "(this Collection)" : e); if (! it.hasNext()) return sb.append(']').toString(); sb.append(',').append(' '); } }
- 0
- 2
- 143
질문&답변
네트워크에 관련되서 질문 드려봅니다 선생님!
맞습니다! 제가 과거에 정리했던 내용을 첨부해봅니다. ARP(Address Resolution Protocol)IP 주소를 MAC 주소로 변환하는 데 사용되는 프로토콜IP주소에 MAC 주소를 매핑 IP 주소를 알고 있을 때 MAC 주소를 찾는 것네트워크 상에서 자신과 통신하고 있는 상대의 (따라서 상대의 IP는 당연히 알고 있는 상태에서) MAC 주소를 찾기 위해 사용되는 프로토콜. 송신 장치가 상대의 IP 주소를 알고 있지만 MAC 주소를 모를 때, 해당 IP 주소를 dest.로 하여 ARP 요청 패킷을 브로드캐스팅하여 “이 IP(dest.)에 해당하는 MAC 주소가 무엇인가요?”라고 물으면 해당 장치가 수신하여 자신의 MAC 주소를 포함한 ARP 응답 패킷을 보낸다.ARP는 IP 데이터 그램을 정확한 목적지 호스트로 보내기 위해 IP에 의해 보조적으로 사용되는 프로토콜이다.
- 0
- 2
- 56
질문&답변
메소드를 추출할 때 static 자동 적용
오 좋은 내용 공유 감사합니다 !!
- 0
- 2
- 134
질문&답변
IOException 예외 처리 질문
예외를 잡는 것뿐만 아니라 던지는 것도 처리하는 방식입니다.다만 아직까지는 IOException에 대해서 자세히 다루지 않고, 스트림 다루기 등이 예제와 강의의 주 목적이기 때문에 코드가 좀 길어진다 싶으면 던지기도 하고, 어쩔 때는 잡기도 하면서 처리하시는 것으로 생각됩니다.결론은 (아직 IOException 예외 처리가 주 목적이 아니기 때문에) 코드가 짧으면 잡아서 처리하고, 코드가 좀 길어지면 던져서 처리한다고만 받아들이시면 될 것 같네요.
- 0
- 2
- 103
질문&답변
숫자와 문자의 2진수 숫자가 동일할 경우 타입에 대한 메타정보는 어디에 있나요
데이터타입이 숫자 타입인지 문자 타입인지 나타내는 byte 도 있을 것 같은데-> 이 부분은 데이터가 저장된 메모리에 함께 보관되는 것이 아니라, .java 코드 파일을 기반으로 해석하게 됩니다.예를 들어 0100 0001(10진수 65)가 메모리 상의 어딘가에 위치해 있다고 합시다. 만약 int value = 65; 형태로 자바 코드에 작성되어 있다면int value 65; // 메모리 상에 0100 0001로 보관 System.out.println(value); // prints 65만약 char = 65; 형태로 자바 코드에 작성되어 있다면char value = 65; // 메모리 상에 0100 0001로 보관 System.out.println(value); // prints 'A'
- 0
- 2
- 140
질문&답변
선생님 혹시 자바 공부나 개발에 대한 책 추천 해주실 수 있으십니까
이펙티브 자바 한번 서점에서 읽어 보시고 결정하세요!
- 0
- 2
- 218
질문&답변
ExecutorServic 우아한 종료 isTerminated()에 대해서 질문
작성하신 문장 중 볼록 된 부분 입니다. 문장 바로 위에 있는 사진을 기준으로 설명드립니다.awaitTermination()은 블로킹 메서드입니다. 또한 해당 메서드는 '종료 기능'을 가진 메서드가 아니고, '종료될 때까지 기다리는' 메서드일 뿐입니다.shutdown(), shutdownNow() 등은 논블로킹 메서드입니다. 따라서 해당 메서드를 호출한 메서드는 그 직후에 본인의 코드들을 이어서 계속 수행합니다.line 3510초를 블로킹 대기했으나, 작업들이 아직 수행중이기에 false가 리턴됩니다. 따라서 line 37의 로그가 실행됩니다.line 38shutdownNow()를 호출합니다. 따라서 스레드 풀에서 수행중인 작업에 대해서는 인터럽트를 발생시키고, 대기중인 작업에 대해서는 컬렉션으로 리턴하는 방식으로 종료 작업을 진행합니다.이때 논블로킹 메서드이므로 요청 스레드인 main 스레드는 곧바로 본인의 다음 코드인 line 44를 실행합니다.line 44스레드풀은 위에서 언급한 종료 작업을 아직 진행중입니다. 즉 아직 종료된 상태가 아니고, 종료를 위해 준비하고 있는 상태입니다. 따라서 es.isTerminated()는 false를 리턴합니다.line 48다시 10초를 블로킹 대기합니다. 기다리는 10초 동안 종료 작업은 잘 마무리되었습니다.따라서 line 53의 es.isTerminated()는 true를 리턴합니다.
- 0
- 2
- 88
질문&답변
future.get()
[스레드풀의 스레드가 작업을 시작하는 시점]은 [get()을 호출하는 시점]이 아니고, [submit()을 호출하는 시점]입니다. 또한 submit()은 non-blocking method이므로 main 스레드는 스레드 풀에게 작업을 수행해달라는 명령만 전달하고, 본인의 스택에 있는 코드를 이어서 수행할 수 있습니다.Future future1 = es.submit(task1); // pool-1-thread-1 작업 시작. non-blocking Future future2 = es.submit(task2); // pool-1-thread-2 작업 시작. non-blocking // non-blocking이므로 main에 이어지는 코드들이 계속 수행... // ... log(end - st + "ms"); // 마찬가지로 수행됨 Integer sum2 = future2.get(); // ...
- 0
- 2
- 209
질문&답변
단일 쓰레드에서 성능 차이가 발생하는 이유
먼저 질문 내용 중에서 BasicInteger와 VolatileInteger의 경우에는 캐시를 사용하는가, 메인 메모리를 사용하는가의 차이로 성능 문제가 발생했다는 것은 알겠는데 ...라고 해 주셨으니 거기에 맞춰 설명드립니다.synchronized 키워드를 명시하여 사용하는 임계 구역은 '여러 스레드가 접근할 수 있다. 단, 락을 획득한 스레드만 (즉 동시에 최대 한 스레드만) 접근하여 연산을 수행할 수 있다'라는 설명도 이해가 가시나요?위 설명이 이해가 가신다는 전제 하에 마저 설명을 드리자면 여러 스레드가 접근할 수 있다 == 메인 메모리에 존재한다 입니다.여기까지 이해가 되셨다면, 결국 BasicInteger와 SyncInteger 간의 성능 차이도 캐시를 사용하는가, 메인 메모리를 사용하는가의 차이로 발생한다는 것도 아실 수 있습니다. (조금 비약이 있는 설명이지만 질문 내용에 답변을 드리기 위해서는 이 정도면 충분하다고 생각하여 다음과 같이 설명드리겠습니다. 더 궁금하시면 구글에 검색하시면 여러 내용들이 있어요.)정리하면 캐시 메모리는 각 스레드마다 가지고 있는 종속적인 공간입니다. Thread-1은 Thread-2의 캐시에 접근할 수 없습니다.그리고 메인 메모리는 아무 스레드나 접근 가능한 공간입니다. Thread-1과 Thread-2은 (권한이 있다는 가정하에) 동일한 메모리 영역에 접근할 수 있습니다.따라서 메인 메모리 상에 존재하는 임계 영역에도 (락만 가지고 있다면) Thread-1과 Thread-2 둘 다 접근이 가능합니다.
- 0
- 2
- 141
질문&답변
문제와 풀이2번 궁금증
t1.join()은 main()에서 호출했습니다.t1.join()은 t1의 run() 메서드에서 정의한 모든 코드가 수행될 때까지 t1.join() 다음에 있는 코드를 수행하지 않도록 합니다.따라서 main()에서 t1.join() 이전에 있는 t1.start(), t2.start(), t3.start()는 t1의 모든 코드가 실행되었는지 여부에 구애받지 않고 모두 정상적으로 작동합니다.t1 스레드를 비롯하여 모든 t 스레드는 로그를 출력 후 1초간 대기합니다.1초라는 시간은 다른 t2, t3 스레드가 한 줄의 로그를 출력하는 데에는 충분한 시간입니다. 따라서 t1이 1초 대기하는 동안 t2, t3도 각자 로그를 출력하고 1초를 대기합니다. 그러면 또 t2, t3가 대기하는 시간동안 t1의 1초 대기가 끝나고 다음 로그를 출력하고, ...따라서 다음과 같은 출력 결과를 얻습니다.16:58:43.144 [ t1] 1 16:58:43.144 [ t2] 1 16:58:43.144 [ t3] 1 16:58:44.152 [ t1] 2 16:58:44.152 [ t2] 2 16:58:44.152 [ t3] 2 16:58:45.154 [ t1] 3 16:58:45.154 [ t2] 3 16:58:45.154 [ t3] 3 모든 스레드 실행 완료. 수행 시간: 3초 Process finished with exit code 0
- 1
- 2
- 97