Hash Table
해시함수를 사용해서 변환한 값을 index로 삼아 key-vlaue를 저장하는 자료구조
기본연산으로는 탐색(Search), 삽입(Insert), 삭제(Delete)가 있다
Direct Addressing Table
가장 간단한 형태의 해시테이블로 key값을 직접적으로 주소로 사용하는 테이블
찾고자하는 key값만 알고있으면 즉시 위치를 찾을 수 있으므로, 탐색,저장,삭제,갱신은 모두
선형시간인 O(1)로 매우 빠른속도로 처리 가능하지만, 한계점 존재함
제네릭을 사용하면 잘못된 타입이 들어올 수 있는 것을 컴파일 단계에서 방지가능하다.
클래스 외부에서 타입을 지정해주기 때문에 따로 타입을 체크하고 변환해줄 필요가 없다.(관리의 용이)
비슷한 기능을 지원하는 경우 코드의 재사용성이 높아진다.
반드시 한글자일 필요는 없다. 다만 대중적으로 통상하는 통상적 선언이다.
public class ClassName<T> { … }
public Interface InterfaceName<T> { … }
기본적인 제네릭타입의 클래스나 인터페이스는 위와같이 선언한다. T타입은 해당 블럭 { … } 안에서까지 유효하다.public class ClassName <T, K> { ... }
public Interface InterfaceName <T, K> { ... }
// HashMap의 경우 아래와 같이 선언되어있을 것이다.
public class HashMap <K, V> { ... }
제네릭 타입을 두개로 둘 수도있다. ( 대표적으로 타입인자를 두개 받는 HashMap ) 이렇게 생성된 제네릭클래스를 사용할 땐public class ClassName <T, K> { ... }
public class Main {
public static void main(String[] args) {
ClassName<String, Integer> a = new ClassName<String, Integer>();
}
}
이런식으로 된다. T는 String이 되고 K는 Integer가 되는 것이다. 주의해야 할 점은 타입 파라미터로 명시할 수 있는 것은 참조타입(Reference Type)밖에 올 수 없다. 즉 int, double, char 같은 primitive type은 올 수 없다. 그래서 Integer, Dobule 같은 Wrapper Type으로 사용한다. 제네릭클래스// 제네릭 클래스
class ClassName<E> {
private E element; // 제네릭 타입 변수
void set(E element) { // 제네릭 파라미터 메소드
this.element = element;
}
E get() { // 제네릭 타입 반환 메소드
return element;
}
}
class Main {
public static void main(String[] args) {
ClassName<String> a = new ClassName<String>();
ClassName<Integer> b = new ClassName<Integer>();
a.set("10");
b.set(10);
System.out.println("a data : " + a.get());
// 반환된 변수의 타입 출력
System.out.println("a E Type : " + a.get().getClass().getName());
System.out.println();
System.out.println("b data : " + b.get());
// 반환된 변수의 타입 출력
System.out.println("b E Type : " + b.get().getClass().getName());
}
}
ClassName 이란 객체를 생성 할 때,<>안에 타입파라미터를 지정한다. 그러면 a객체의 ClassName의 E 제네릭 타입은 String으로 모두 변환된다. 반대로 b객체의 ClassName의 E 제네릭 타입은 Integer로 모두 변환된다. < 결과 >입력된 N의 크기에 따라 실행되는 조작의 수를 말한다. 표기법 (Big-O)
a. O(1) - 상수시간 : 입력값 n이 주어졌을 때 연산 1번만 수행
b. O(log N) - 로그시간 : 입력값 n이 주어졌을 때, 특정한 요건에 따라 필요한 단계가 연산이 줄어든다. (이진트리검색)
c. O(n) - 직선적 시간 : 문제를 해결하기 위한 단계의 수와 입력값 n이 1:1관계 (반복문)
d. O(n^2) - 2차 시간 : 문제를 해결 하기 위한 단계의 수는 입력값의 제곱(이중 반복문)
시간복잡도에서 중요하게 보는 것은 가장 큰 영향을 미치는 n의 단위이다.
e. O(C^n) - **지수 시간 : 문제를 해결하기 위한 단계의 수는 주어진 상수 C의 n제곱**
실행시간은
O(1) < O(log n) < O(n) < O(n log n) < O(n^2) < O(2^n) < O(n!) < O(n^n) 순으로 오래걸린다.
ArrayList는 내부인덱스를 이용하여 검색이 한번에 이루어지기 때문에 빠른검색속도를 보장하지만, 데이터 추가 / 삭제 시 많은 데이터가 밀리거나 당겨지기 때문에 많은 시간이 소요된다.
(검색 강점)
LinkedList는 추가 / 삭제 시 인근 노드들의 참조값만 수정해 줌으로써 빠른 처리가 가능하지만, 데이터를 검색할 경우 해당 노드를 찾기 위해 처음부터 순회검색을 해야 하기 때문에, 데이터의 수가 많아질수록 효율이 떨어진다.
(데이터 추가/삭제 강점)
Stack st = new Stack(); // 타입 설정x Object로 선언
Stack<StackDemo> demo = new Stack<StackDemo>(); // class타입으로 선언
Stack<Integer> i = new Stack<Integer>(); // Integer타입 선언
Stack<Integer> i2 = new Stack<>(); // 뒤의 타입 생략 가능
Stack<String> s = new Stack<String>(); // String타입 선언
Stack<Character> ch = new Stack<Character>(); // Char타입 선언
스택의 선언방법이다.
Stack<타입> 변수명 = new Stack<타입>();
타입선언을 생략해도 되지만 처음들어간 타입으로 입력을 계속하지않으면 타입오류가 발생하므로 명확하게 선언해주는것이 좋다.
- push() :값을 추가한다.
- pop() : 값을 삭제한다. <맨마지막에 들어간값 삭제>
- clear() : 초기화
- firstElement() : 처음 값 불러오기
- lastElement() : 마지막값 불러오기
- peek() : 마지막값 불러오기
- get(int i) : 스택의 index값 출력
- contains(Object) : 값의 여부를 찾아 true, false 반환
- indexOf(int index) : 값의 위치를 반환
import java.util.Iterator;
import java.util.Stack;
public class StackDemo {
public static void main(String[] args) {
Stack<String> s = new Stack<String>();
// Stack 값 추가
s.push("Hello");
s.push("World");
// firstElement(), lastElement(), peek()을 사용 -> 처음, 마지막, 마지막 값을 불러온다
System.out.println("처음 값 : " + s.firstElement());
System.out.println("마지막 값 : " + s.lastElement());
System.out.println("마지막 값 : " + s.peek());
// get(i) 메서드를 사용하여 Stack의 Index 값을 출력
for(int i = 0; i < s.size(); i++)
System.out.print(s.get(i) + " ");
System.out.println();
// 향상된for문을 사용하여 Stack의 값을 출력
for(String str: s)
System.out.print(str + " ");
// Iterator를 사용하여 Stack의 값을 출력
Iterator iter = s.iterator();
while(iter.hasNext())
System.out.print(iter.next() + " ");
}
}
Enqueue : 큐 맨 뒤에 데이터 추가
Dequeue : 큐 맨 앞쪽의 데이터 삭제
- 큐 한쪽 끝은 Front로 정해 삭제 연산만 수행
- 다른 한쪽 끝은 Rear로 정해 삽입연산만 수행
- 그래프의 넓이 우선탐색(BFS)에서 사용
- 컴퓨터 버퍼에서 주로 사용, 마구 입력이 되었으나 처리하지 못할 때, 버퍼(큐)를 만들어 대기
import java.util.LinkedList; //import
import java.util.Queue; //import
Queue<Integer> queue = new LinkedList<>(); //int형 queue 선언, linkedlist 이용
Queue<String> queue = new LinkedList<>(); //String형 queue 선언, linkedlist 이용
비-선형구조
노드 : 트리에서 각각의 구성요소
레벨 : 트리에서 각각의 층을 나타내는 단어 ( 루트 노드는 0 )
어떻게 저장???
이진트리(자식노드가 최대 2개인 트리)
— Node
— Tree
Tree Class
package Tree;
public class Tree {
int count;
public Tree() {
count = 0;
}
public class Node {
Object data;
Node left;
Node right;
// 생성 시 매개변수를 받아 초기화하는 방법으로만 선언 가능
public Node(Object data) {
this.data = data;
left = null;
right = null;
}
public void addLeft(Node node) {
left = node;
count++;
}
public void addRight(Node node) {
right = node;
count++;
}
public void deleteLeft() {
left = null;
count--;
}
public void deleteRight() {
right = null;
count--;
}
}
public Node addNode(Object data) {
Node n = new Node(data);
return n;
}
public void preOrder(Node node) {
if(node == null) {
return;
}
System.out.print(node.data + " ");
preOrder(node.left);
preOrder(node.right);
}
public void inOrder(Node node) {
if(node == null) {
return;
}
inOrder(node.left);
System.out.print(node.data + " ");
inOrder(node.right);
}
public void postOrder(Node node) {
if(node == null) {
return;
}
postOrder(node.left);
postOrder(node.right);
System.out.print(node.data + " ");
}
}
Run Class
import Tree.*;
import Tree.Tree.Node;
public class Run {
public static void main(String[] args) {
// 트리 생성
Tree tree = new Tree();
// 노드 생성
Node node1 = tree.addNode(1);
Node node2 = tree.addNode(2);
Node node3 = tree.addNode(3);
Node node4 = tree.addNode(4);
Node node5 = tree.addNode(5);
Node node6 = tree.addNode(6);
Node node7 = tree.addNode(7);
// 트리 연결관계 생성
/* 트리 모양
* 1
* 2 3
* 4 5 6 7
*/
node1.addLeft(node2);
node1.addRight(node3);
node2.addLeft(node4);
node2.addRight(node5);
node3.addLeft(node6);
node3.addRight(node7);
// 순회
tree.preOrder(node1);
System.out.println();
tree.inOrder(node1);
System.out.println();
tree.postOrder(node1);
System.out.println();
// 삭제
node2.deleteLeft();
node3.deleteRight();
/* 삭제 이후 트리 모양
* 1
* 2 3
* 5 6
*/
// 순회
System.out.println();
tree.preOrder(node1);
System.out.println();
tree.inOrder(node1);
System.out.println();
tree.postOrder(node1);
System.out.println();
}
}
참조: https://readerr.tistory.com/35
Error 및 RuntimeException을 상속하지 않는 클래스는 Checked Exception,
상속한 클래스는 Unchecked Exception.
@FunctionalInterface
interface MyInterface {
void display();
void display(int x, int y);
}
public class LambdaExpression {
public static void main(String[] args) {
MyInterface ref = () -> System.out.print("It is display from sout");
ref.display(2, 3);
}
}
[https://stackoverflow.com/questions/23342499/why-functional-interfaces-in-java-8-have-one-abstract-method]