반응형

최근 읽고 있는 '자바 최적화'라는 책을 보다가 몰랐었던 내용이 있어 기록할겸 남겨본다. 

자바7 이전 리소스 사용후 닫는 것은 온전히 개발자의 몫

    public void readFirstLineOld(File file) throws IOException {
        BufferedReader reader = null;
        try {
            reader = new BufferedReader(new FileReader(file));
            String FirstLine = reader.readLine();
        } finally {
            if (reader != null) {
                reader.close();
            }
        }
    }

 

자바7 부터 언어 자체에 추가된 try-with-resources 생성자를 이용하면 try키워드 다음의 괄호 안에 리소스(AutoCloseable 인터페이스를 구현한 객체만 가능)를 지정해서 생성할 수 있다. 이로써 try 블록이 끝나는 지점에 개발자가 close() 메서드 호출을 깜빡 잊고 빠뜨려도 자동으로 호출된다. close() 메서드는 방금 전 예제와 똑같이 호출되고 비지니스 로직의 예외 발생 여부와 상관없이 무조건 실행된다.

    public void readFirstLineOld(File file) throws IOException {
        try( BufferedReader reader = new BufferedReader(new FileReader(file))) {
            String FirstLine = reader.readLine();
        }
    }

코드가 훨씬 심플해졌다. 하지만 try-with-resources 생성자 사용시 자동으로 close() 를 호출해주는 것을 몰랐다면 finally 코드를 생성해 그 안에서 또 close를 호출하려고 하였을거다...

위의 예에서는 catch(IOException exception) {} 과 같이 별도로 에러처리를 안해주고 그냥 상위로 exception을 그냥 던져버리는데 사실 좋지 않다. 항상 에러 발생시 catch 내부에서 잡고 로깅해주고 처리해주는 것이 좋다.

이번 포스팅을 하며 느낀점은 기본에 좀 더 충실한 공부가 필요할 것 같다.

 

반응형
반응형

자바 스트림 Skip 사용시 java.lang.IllegalArgumentException: -number 형태의 에러가 나는 이유는

skip 메서드의 인자로 0보다 작은 값이 들어 갔기 때문이다.

따라서 어떤 수치를 계산해서 skip에 인자를 전달하고 있다면 해당 값이 0보다 작지 않은 지 확인해보자.

skip 내부 로직

반응형
반응형



HashMap을 생성하고 그 안에 put을 해도 HashMap자체의 해쉬코드(hashCode)값은 안변할 줄 알았것만.....


결론은 put을 할 때마다 hadhCode값이 변하게 됩니다.


[ 백문이불여일코딩 ]


[ 결과 hashcode 값 ]

get메서드사용할때는 당연히 안바뀌고 put으로 데이터를 넣을 때 마다 해쉬코드가 변하는 것을 확인!!!!

값은 key값에 데이터를 덮어씌울때도 hashcode가 변한다! 이유는??


[ 이유는??? ]

HashMap의 put메서드가 내부적으로 entry를 새로 만들기 때문!!!!

map에서의 hashcode의 값은 entry의 해시코드의 합으로 정의되는데 내부에서 

put메서드가 호출될때마다 새로운 entry를 생성하기 때문에 값이 달라짐

public int hashCode()
Returns the hash code value for this map. The hash code of a map is defined to be the sum of the hash codes of each entry in the map's entrySet() view. This ensures that m1.equals(m2) implies that m1.hashCode()==m2.hashCode() for any two maps m1 and m2, as required by the general contract of Object.hashCode().

This implementation iterates over entrySet(), calling hashCode() on each element (entry) in the set, and adding up the results.

Specified by:
hashCode in interface Map<K,V>
Overrides:
hashCode in class Object
Returns:
the hash code value for this map
See Also:
Map.Entry.hashCode(), Object.equals(Object), Set.equals(Object)


참조 : https://docs.oracle.com/javase/7/docs/api/java/util/AbstractMap.html#hashCode()


[ HashMap put메서드를 따라가보자! ]

1. put


2. putVal


3. putTreeVal


4. newTreeNode


5. new TreeNode



6. TreeNode의 super


Wow....


상식적으로 생각했을 때 Map이 한 번 생성되면 안에 내용이 변경되더라도 해시코드(hashcode)값은 변하지 않을 줄 알았는데....


변하는 것을 보고 한 번 끝까지 따라가 보았다.


뭔가 속이 후련한???


앞으로도 궁금한점이 생긴다면 대충 알고 넘어가는 것보다 한 번 끝까지 따라가 제대로 된 이유를 알아보는것을 습관화하도록 하자!






반응형
반응형


Heap Dump를 볼 일이 생겨서 Eclipse Memory Analyzer(MAT) 설치 후 실행을 하려고 하는데 다음과 같은 에러가 발생하였다.


'An error has occurred. See the log file~~~'



그래서 해당 위치에 가서 log파일을 열어 보니 다음과 같은 에러가 발생한 상태였다...


!SESSION 2019-01-31 16:45:23.495 -----------------------------------------------

eclipse.buildId=unknown

java.version=1.8.0_151

java.vendor=Oracle Corporation

BootLoader constants: OS=macosx, ARCH=x86_64, WS=cocoa, NL=ko_KR

Framework arguments:  -keyring /Users/nhnent/.eclipse_keyring

Command-line arguments:  -os macosx -ws cocoa -arch x86_64 -keyring /Users/nhnent/.eclipse_keyring


!ENTRY org.eclipse.osgi 4 0 2019-01-31 16:45:27.178

!MESSAGE Application error

!STACK 1

java.lang.IllegalStateException: The platform metadata area could not be written: /private/var/folders/72/tlj3dkwx5vx_4tvvmkt9v3nm0000gn/T/AppTranslocation/2AB9061A-0D7D-4CE6-AA94-5D14138D7687/d/mat.app/Contents/MacOS/workspace/.metadata.  By default the platform writes its content

under the current working directory when the platform is launched.  Use the -data parameter to

specify a different content area for the platform.

        at org.eclipse.core.internal.runtime.DataArea.assertLocationInitialized(DataArea.java:70)

        at org.eclipse.core.internal.runtime.DataArea.getStateLocation(DataArea.java:138)

        at org.eclipse.core.internal.preferences.InstancePreferences.getBaseLocation(InstancePreferences.java:44)

        at org.eclipse.core.internal.preferences.InstancePreferences.initializeChildren(InstancePreferences.java:209)

        at org.eclipse.core.internal.preferences.InstancePreferences.<init>(InstancePreferences.java:59)

        at org.eclipse.core.internal.preferences.InstancePreferences.internalCreate(InstancePreferences.java:220)

        at org.eclipse.core.internal.preferences.EclipsePreferences.create(EclipsePreferences.java:349)

        at org.eclipse.core.internal.preferences.EclipsePreferences.create(EclipsePreferences.java:337)

        at org.eclipse.core.internal.preferences.PreferencesService.createNode(PreferencesService.java:393)

        at org.eclipse.core.internal.preferences.RootPreferences.getChild(RootPreferences.java:60)

        at org.eclipse.core.internal.preferences.RootPreferences.getNode(RootPreferences.java:95)

        at org.eclipse.core.internal.preferences.RootPreferences.node(RootPreferences.java:84)


구글링 결과 zip압축파일을 푼 실행파일의 위치를 Applications폴더로 이동시켜주어 해결했다는 글을 발견!!!


이에 바로 Documents폴더에서 Applications폴더로 이동 후 실행하니 정상적으로 실행되었다~


혹시나 같은 문제가 발생하시는 분들은 폴더 이동시켜서 실행해보세요:)


반응형
반응형

자바 Stream에서 Map, Object내부에 또 다른 Map, Set, List와 같은 Collection이 있을 때 각 value를 개별된 값으로 처리하고 싶은 경우



Student POJO

public class Student {

private String name;
private Set<String> book;

public void addBook(String book) {
if (this.book == null) {
this.book = new HashSet<>();
}
this.book.add(book);
}
//getters and setters

}



flatMap() and Set example

1차적으로 map으로 POJO에 Set에 담긴 Book데이터를 가져와 flatmap으로 펼친 후 list로 collect

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

public class TestExample2 {

public static void main(String[] args) {

LexiconMain.Student obj1 = new LexiconMain.Student();
obj1.setName("mkyong");
obj1.addBook("Java 8 in Action");
obj1.addBook("Spring Boot in Action");
obj1.addBook("Effective Java (2nd Edition)");

LexiconMain.Student obj2 = new LexiconMain.Student();
obj2.setName("zilap");
obj2.addBook("Learning Python, 5th Edition");
obj2.addBook("Effective Java (2nd Edition)");

List<LexiconMain.Student> list = new ArrayList<>();
list.add(obj1);
list.add(obj2);

List<String> collect =
list.stream()
.map(x -> x.getBook()) //Stream<Set<String>>
.flatMap(x -> x.stream()) //Stream<String>
.distinct()
.collect(Collectors.toList());

collect.forEach(x -> System.out.println(x));
}

}


Output

Spring Boot in Action
Effective Java (2nd Edition)
Java 8 in Action
Learning Python, 5th Edition


Stream관련 자세한 예제가 더 궁금하다면 아래 링크로


참고 : https://www.mkyong.com/java8/java-8-flatmap-example/

반응형
반응형

[ Java8 ] Stream Collectors toMap 사용시 Duplicate Key Error


DB로부터 데이터를 가져와 List에 담고 List에 담긴 데이터를 MAP에 담아 처리하는 작업이다.


List to Map 시 Key 값이 중복일 경우 Duplicate Key에러 발생

2018:08:08 19:47:49.947 ERROR --- [http-nio-80-exec-24] o.a.c.c.C.[.[.[.[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is java.util.concurrent.CompletionException: java.lang.IllegalStateException: Duplicate key 31356962-6f5b-4280-9beb-f48d4c437695] with root cause

java.lang.IllegalStateException: Duplicate key 31356962-6f5b-4280-9beb-f48d4c437695

        at java.util.stream.Collectors.lambda$throwingMerger$0(Collectors.java:133)

        at java.util.HashMap.merge(HashMap.java:1254)



[ 에러가 발생했던 코드 ]

CompletableFuture<List<Partner>> matchedList = cookieMatchingRepository.getMatchedPartnerUserIdByBid(bid);
Map<String, String> matchedPartnerMap =
matchedList.get().stream().collect(Collectors.toMap(Partner::getPartnerId, Partner::getPartnerUserId));


[ 동일한 KEY값이 들어와도 처음 KEY-VALUE 값으로 처리하는 코드 (노란 부분이 바뀜) ]

CompletableFuture<List<Partner>> matchedList = cookieMatchingRepository.getMatchedPartnerUserIdByBid(bid);
Map<String, String> matchedPartnerMap = matchedList.get().stream()
.collect(Collectors.toMap(Partner::getPartnerId, Partner::getPartnerUserId, (p1, p2) -> p1));



반응형
반응형


리눅스에서 openjdk 업그레이드 하기!!!


1. 자바 버전 확인

$ java -version


2. 설치 가능한 openjdk 버전 확인

$ yum list java*jdk-devel



3. 설치고자하는 버전을 확인 후 설치

$ yum install -y java-1.8.0-openjdk-devel.x86_64


나의 경우에는 설치하고 java -version 명령어를 통해 확인했을 때 잘 업그레이드 된 걸 확인할 수 있었다.



혹시 바뀌지 않은 경우 다음과 같이 바꾸도록 하자.

$ /usr/sbin/alternatives --config java



해당 명령어를 쳐서 원하는 버전의 번호를 선택해주면 된다~!!!!



반응형
반응형


Java8 Stream을 사용하다 보면 자주 발생할 수 있는 미묘한 실수 중에 하나는


이미 사용했던 Stream을 다시 사용하려고 하는 것이다. Stream은 오직 한 번만 사용할 수 있다.


Stream should be operated on (invoking an intermediate or terminal stream operation) only once. 


A Stream implementation may throw IllegalStateException if it detects that the Stream is being reused.


이미 사용했던 Stream을 또 사용하게 될 경우 다음과 같은 에러 로그를 발견할 수 있을 것이다.


java.lang.IllegalStateException: stream has already been operated upon or closed

at java.util.stream.AbstractPipeline.sourceStageSpliterator(AbstractPipeline.java:279)

at java.util.stream.ReferencePipeline$Head.forEach(ReferencePipeline.java:580)

at com.payco.cm.service.CacheService.cachingPartnerUrlMap(CacheService.java:31)

at com.payco.cm.service.CacheService$$FastClassBySpringCGLIB$$d06ada09.invoke(<generated>)

at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)

at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:738)

at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157)

at org.springframework.cache.interceptor.CacheInterceptor$1.invoke(CacheInterceptor.java:52)

at org.springframework.cache.interceptor.CacheAspectSupport.invokeOperation(CacheAspectSupport.java:344)

at org.springframework.cache.interceptor.CacheAspectSupport.execute(CacheAspectSupport.java:407)

at org.springframework.cache.interceptor.CacheAspectSupport.execute(CacheAspectSupport.java:326)

at org.springframework.cache.interceptor.CacheInterceptor.invoke(CacheInterceptor.java:61)

at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)

at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:673)



[ 문제가 발생했던 코드 ]



코드를 보면 Stream을 두 번 사용하고 있는 것을 볼 수있다.


로깅기능으로 잠깐 보려고 했던 코드가 문제가 되었다.


정리하자면

Stream shoud be used only Once!!!


반응형
반응형

Java NanoTime To Seconds


보통 특정 메서드를 실행하거나 로직을 수행할 때 시간이 얼마나 걸리는지 측정(성능측정)하기 위해서 nanotime을 자주사용한다.

long start = System.nanoTime();


하지만 나노타임(nanotime)은 우리가 보고 판단하기에는 너무 어렵다...초로 바꿔보자...


자바에서 nanotime을 second(초)로 변환하는 방법은 두 가지이다.


1. TimeUnit.SECONDS.convert(nanotime, TimeUnit.NANOSECONDS을 이용하는 방법


이 방법은 진짜 딱 초단위 까지만 나오게 된다. 11.035초의 시간이 걸렸더라도 딱 11초만 표기된다.


2.((double) nanotime) / 1000000000; 을 사용하는 방법


이 방법은 11.33599 형태로 double형 단위에 맞는 초단위로 표기된다.



반응형

+ Recent posts