이번 주 공부할 내용인 해시 테이블 자료구조에 대해 정리해보겠습니다. 먼저 해시 테이블과 해싱(Hashing)이 무엇인지 알아보겠습니다. 그다음 자바에서 어떻게 적용되어 있는지 알아본 후 대표적인 클래스와 그 메서드를 알아보겠습니다.
해시 테이블(Hash Table)
해시 테이블이란 키와 값의 쌍으로 이루어진 자료구조입니다. 해당 자료구조에는 해싱이라는 과정을 통해서 데이터가 저장됩니다. 그리고 원리적으로는 데이터 양이 아무리 많아지더라도 자료의 검색과 삭제에 O(1)의 시간이 걸립니다. 때문에 커다란 데이터에서 특정한 값을 검색할 때 해시 테이블을 사용하는 것이 유리합니다.
여기서 해싱(Hashing)이란 해시 함수(Hash Function)를 이용해 데이터를 저장하고 검색하는 기법을 말합니다. 해시 함수는 임의의 길이의 데이터를 고정된 길이의 데이터로 매핑하는 함수를 말합니다. 예를 들어 해시 함수 중 하나인 SHA-256의 경우 해당 함수에 한 글자를 넣던 백 글자를 넣던 입력값의 길이에 상관없이 256비트의 결괏값이 출력됩니다. 좀 더 간단히는 아래의 그림과 같이 표현할 수 있습니다.
해시 테이블은 자료 검색에 사용하기 위한 자료구조라 키값에 중복이 있으면 안 됩니다. 하지만 해시 함수는 어떤 값을 입력으로 넣든 간에 같은 길이의 결괏값을 반환하기 때문에 낮은 확률일지라도 서로 다른 값을 입력으로 넣었지만 같은 값이 출력되는 경우가 있습니다.
위의 그림과 같으며 이를 해시 충돌이라고 합니다. 해시 충돌을 방지하기 위해서 체이닝이나 이중 해시같은 방법이 사용됩니다. 다음과 같이 간단히 해시에 대해 알아보았습니다. 다음으로는 자바에서의 해시에 대해 알아보겠습니다.
자바에서의 해싱
자바에서 해싱을 구현한 클래스로는 HashSet, HashMap, Hashtable 등이 있습니다. 이중 컬렉션 프레임워크가 등장하기 전에 사용하던 Hashtable은 이전 소스와의 호환성 문제로 남겨두고 있으니 이제는 HashMap을 사용하는 것이 좋습니다. 그리고 HashSet은 리스트와 달리 저장 순서를 유지하지 않습니다. 저장 순서를 유지하고자 한다면 LinkedHashSet을 사용하면 됩니다.
자바에서 해싱을 사용하는 자료구조는 배열과 LinkedList의 조합으로 되어 있습니다. 해시 함수를 통해 나온 해시 코드는 키로써 배열의 인덱스가 되고 값은 해당 배열에 연결된 LinkedList에 저장됩니다. 때문에 키값으로 배열의 해당 인덱스에 있는 연결 리스트에 있는 값을 빠르게 찾을 수 있습니다.
해시 함수에서 해시 충돌이라는 것에 대해서 이야기를 했었습니다. 자바에서는 해시 함수로 Object 클래스에 정의된 hashCode() 메서드를 사용합니다. 해당 메서드는 객체의 주소를 이용하는 알고리즘이라 모든 객체에 대해 유일한 값을 반환합니다. 때문에 해시 충돌의 걱정을 덜 수 있습니다. 그리고 String 클래스의 경우에는 오버라이딩한 hashCode() 메서드를 사용합니다. 이 메서드는 같은 내용의 문자열을 가졌다면 같은 해시 코드를 반환합니다. 이상으로 해싱은 마치고 클래스와 메서드에 대해 정리하겠습니다.
Spring Bean Configuration File을 통해 만든 xml의 context:component-scan 태그
- Namespaces에서 context를 체크한다.
<context:component-scan base-package="패키지이름"/>
xml의 context:component-scan 태그 base-package 속성에 패키지 이름을 적어 넣으면
해당 패키지 안에 있는 클래스를 로드하여 @Component 어노테이션에 있는 클래스를 Bean에 등록한다.
@Component 어노테이션의 경우 클래스에 필드가 없는 경우 사용한다.
@Configuration
자바 클래스를 설정으로 이용할 수 있다. 클래스 상단에 어노테이션을 적용한다.
클래스 안의 메서드를 @Bean 어노테이션으로 Bean으로 등록하고 메서드명은 xml 설정의 id와 같다.
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class ApplicationContext {
@Bean
public A methodA() {
A a = new A();
a.setName("이병건");
a.setValue("나이: 41");
return a;
}
@Bean
public B methodB() {
B b = new B();
b.setName("BBB");
b.setA(methodA()); // setter 주입
return b;
}
}
AOP (Aspect Oriented Programming 관점지향)
트랜잭션이나 로깅, 보안과 같이 여러 모듈에서 공통적으로 사용하는 기능을 해당 기능을 분리하여 관리할 수 있다.
Spring Bean Configuration File의 Namespaces에서 aop를 체크한다.
용어
Aspect : 공통의 관심사들로 이루어진 모듈
ex) 로그에 관련된 모듈을 모아둔 클래스/ 메서드 하나가 모듈 하나로 생각하면 된다.
Target : 공통 모듈(Aspect)이 적용될 곳 (클래스 또는 메서드)
Advice : 실제 적용될 공통 모듈 하나를 의미
Joinpoint : advice가 적용되어야 하는 시점
Pointcut : target이 상세화 된 것
<context:component-scan base-package="aopEx01"/>
<bean id="LoggerAOP01" class="aopEx01.Aspect01"/>
<aop:config>
<aop:aspect id="Logger01" ref="LoggerAOP01"> <!-- 참조할 Bean -->
<!-- expression으로 적용될 곳을 지정 (aopEx01 패키지의 A 클래스) -->
<aop:pointcut expression="within(aopEx01.A)" id="pointcutA"/>
<aop:pointcut expression="within(aopEx01.B)" id="PointcutB"/>
<aop:pointcut expression="within(aopEx01.C)" id="PointcutC"/>
<!-- 상단에 pointcutA로 지정한 aopEx01 패키지 A 클래스의
loggerAop01 메서드를 around로 지정 -->
<aop:around method="loggerAop01" pointcut-ref="pointcutA"/>
<aop:before method="loggerAop02" pointcut-ref="PointcutB"/>
<!-- 오류가 나더라도 무조건 호출되는 모듈 aop:after -->
<aop:after method="loggerAop03" pointcut-ref="PointcutC"/>
<!-- 핵심 로직이 정상 종료 되었을 때 호출되는 공통 모듈 -->
<aop:after-returning method="loggerAop03" pointcut-ref="PointcutC"/>
<!-- 핵심 로직이 오류가 발생하여 종료가 되었을 때 호출되는 공통모듈 -->
<aop:after-throwing method="loggerAop03" pointcut-ref="PointcutC"/>
</aop:aspect>
</aop:config>