import java.util.LinkedList;
public class HashTable {
//링크드리스트를 저장하는 배열
private LinkedList<Node>[] data;
// 데이터를 key, value로 저장하는 Node
class Node {
String key;
String value;
public Node(String key, String value) {
this.key = key;
this.value = value;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}
//hashtable의 크기를 미리 선언해둔다
public HashTable(int size) {
this.data = new LinkedList[size];
}
//해시 코드를 key의 각 아스키코드를 더해서 반환한다.
private int getHashCode(String key) {
int hashcode = 0;
for (char c : key.toCharArray()) {
hashcode += c;
}
return hashcode;
}
//해쉬코드를 이용해서 index를 생성한다.
private int convertToIndex(int hashcode) {
return hashcode % data.length;
}
//특정 키를 이용해서 링크드리스트 내 node를 검색하는 메소드
private Node searchNodeByKey(LinkedList<Node> list, String key) {
if (list == null) {
return null;
}
for (Node node : list) {
if (node.key.equals(key)) {
return node;
}
}
return null;
}
//key의 value를 저장하는 메소드
public void put(String key, String value) {
int hashcode = getHashCode(key);
int index = convertToIndex(hashcode);
LinkedList<Node> list = data[index];
if (list == null) {
list = new LinkedList<>();
data[index] = list;
}
Node node = searchNodeByKey(list, key);
if (node == null) {
node = new Node(key, value);
System.out.println("key : " + key + ", hashcode : " + hashcode + " index : " + index);
list.addLast(node);
} else {
node.setValue(value);
}
}
//특정 key를 이용해 value를 반환하는 메소드
public String get(String key) {
int hashcode = getHashCode(key);
int index = convertToIndex(hashcode);
LinkedList list = data[index];
if (list == null) {
return null;
} else {
Node node = searchNodeByKey(list, key);
return node == null ? null : node.value;
}
}
}
public class Main {
public static void main(String[] args) {
HashTable hashTable = new HashTable(5);
hashTable.put("kim","my name is kim");
hashTable.put("park","my name is park");
hashTable.put("ko","my name is ko");
hashTable.put("lee","my name is lee");
hashTable.put("lee","my name is lee lee");
System.out.println(hashTable.get("kim"));
System.out.println(hashTable.get("park"));
System.out.println(hashTable.get("ko"));
System.out.println(hashTable.get("lee"));
System.out.println(hashTable.get("sang"));
}
}
key : kim, hashcode : 321 index : 1
key : park, hashcode : 430 index : 0
key : ko, hashcode : 218 index : 3
key : lee, hashcode : 310 index : 0
my name is kim
my name is park
my name is ko
my name is lee lee
null
Process finished with exit code 0
정규표현식을 이용해서 두가지 작업을 진행할 수 있습니다. 1) 입력된 문자열이 전화번호, 이메일, IP주소, 주민등록번호 등의 형식과 일치하는지 검증할 수 있습니다. 2) 입력된 문자열의 정해진 형식이 있다면 원하는 값을 추출할 수 있습니다. (ex: json, log, html tag 등..)
이번 포스팅에서는 문자열 검증에 대해서 정리해보겠습니다.
2. 정규 표현식 정리
정규 표현식
설명
^
문자열의 시작
$
문자열의 종료
.
임의의 한 문자
*
앞의 문자가 0개 ~ 여러개 있음
ex) [a-z]* 소문자 알파벳을 0개 ~ 여러개 입력 가능
.* 임의의 문자를 0개 ~ 여러개 입력 가능
+
앞의 문자가 1개 ~ 여러개
ex) .+ 임의의 문자를 1개 ~ 여러개 입력 가능
?
앞의 문자가 0개 ~ 1개 있음
ex) .? 임의의 문자를 0개 ~ 1개를 입력할 수 있음
\.? . 문자를 0개 ~ 1개를 입력할 수 있음
[]
문자의 집합이나 범위 안에 속한 한개의 문자를 표현할 때 사용한다. 범위를 나타낼 땐 - 기호를 사용하며 ^문자를 앞에 붙여 부정을 표현할 수 있다.
ex) [a-z&&[^u-x]] 소문자 알파벳 중에서 u ~ x 를 제외한 나머지 알파벳만 입력이 가능하다.
{}
앞 문자의 횟수를 나타낸다
ex) .{3,8} 임의의 문자를 3자에서 8자 사이로 입력해야한다.
[a-z]{3,} 소문자 알파벳 3자 이상 입력해야한다.
()
()는 여러개의 문자를 하나의 덩어리로 인식한다.
ex) (Mon|Tues|Fri)day Monday, Tuesday, Friday를 입력할 수 있습니다.
(([01][0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9])? 00:00:00 ~ 23:59:59 형식으로 입력하거나 빈값을 입력할 수 있다.
|
패턴 안에서 OR 연산을 수행합니다.
\w
숫자, 알파벳, _ 문자
\W
\w를 제외한 문자
\d
숫자(0~9)
\D
\d를 제외한 문자
\s
공백 문자 (\t\n\x\B\f\r)
\S
\s를 제외한 문자
\b
공백을 기준으로 단어를 찾는다. (문자열 검색을 위해 사용되는 듯 하다.)
ex) cat\b cat으로 끝나는 단어를 검색한다.
input : cat concat
\B
\b과 반대
\
. 이나 + 같이 정규 표현식 문자를 문자로 표현하기 위해 문자 앞에 \를 붙여야한다.
ex) \. \+
3. 자바의 정규 표현식
자바에서 정규 표현식을 사용하려면 java.util.regex 패키지의 Pattern클래스와 Matcher클래스를 사용해야 합니다.
Pattern 클래스 : 정규 표현식을 컴파일하는 객체입니다. Mattcher 클래스 : 정규 표현식을 해석하고 입력된 문자열에 대해 일치여부를 판별할 때 사용되는 클래스입니다.
static public void test2() {
//숫자, 알파벳, _!@#$% 문자를 입력할 수 있다.
Pattern pattern = Pattern.compile("[\\w!@#$]{8,13}");
Matcher matcher = pattern.matcher("dasdqw_e#"); //true
System.out.println(matcher.matches());
}
프로그램은 외부에서 데이터를 읽거나 외부로 데이터를 출력하는 작업이 빈번하게 일어납니다. 이때 데이터는 어떠한 통로를 통해서 데이터가 이동되는데, 이 통로를 Stream 이라고 합니다.
자바에는 이러한 기능을 수행하기 위해 InputStream와 OutputStream이 존재하며 단일 방향으로 연속적으로 흘러갑니다. InputStream과 OutputStream은 추상 클래스이며 추상 메소드를 오버라이딩해서 다양한 역할을 수행할 수 있습니다. (예 : 파일, 네트워크, 메모리 입출력)
자바에서 기본적으로 제공하는 I/O 기능은 java.io 패키지에서 제공됩니다. InputStream은 외부에서 데이터를 읽는 역할을 수행하고, OutputStream은 외부로 데이터를 출력하는 역할을 수행합니다.
2. InputStream
바이트 기반 입력 스트림의 최상위 추상클래스입니다. (모든 바이트 기반 입력 스트림은 이 클래스를 상속받습니다.) 파일 데이터를 읽거나 네트워크 소켓을 통해 데이터를 읽거나 키보드에서 입력한 데이터를 읽을 때 사용합니다. InputStream은 읽기에 대한 다양한 추상 메소드를 정의해 두었습니다. 그리고 InputStream의 추상메소드를 오버라이딩하여 목적에 따라 데이터를 입력 받을 수 있습니다.
3. InputStream의 기본 메소드
public abstract int read() throws IOException;
public void read() throws IOException {
byte[] bytes = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11};
InputStream is = new ByteArrayInputStream(bytes);
int data;
while ((data = is.read()) != -1) {
System.out.print(data);
}
}
입력 스트림에서 1 바이트 씩 읽고 읽은 바이트를 반환합니다. 데이터를 모두 읽었다면 -1을 반환합니다.
출력 ------------ 1234567891011
public int read(byte buffer[]) throws IOException {
return read(buffer, 0, buffer.length);
}
public void read() throws IOException {
byte[] bytes = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11};
InputStream is = new ByteArrayInputStream(bytes);
int len = 3;
int readCnt = 0;
byte[] buffer = new byte[len];
while ((readCnt = is.read(buffer)) != -1) {
for (int index = 0; index < readCnt; index++) {
System.out.print(buffer[index]);
}
System.out.println();
}
}
입력 스트림에서 byte buffer[] 만큼 읽고 읽은 데이터 수를 반환합니다. 여러 바이트 씩 읽기 때문에 한 바이트 씩 읽는 것 보다 더 빠릅니다.
데이터를 모두 읽었다면 -1을 반환합니다.
위 소스는 bytes에 있는 데이터를 buffer의 크기(3)만큼 읽어서 buffer에 넣고 콘솔에 출력하고 read(buffer)의 반환값이 -1이 될 때까지 반복합니다.
read() 메소드와 다른 점은 read() 메소드의 반환 값이 읽은 데이터가 아니라 읽은 데이터 수 라는 점입니다. 읽은 데이터는 매개변수로 넘긴 buffer에 저장됩니다.
출력 ------------ 123 456 789 1011
public int read(byte buffer[], int off, int len) throws IOException
public void read() throws IOException {
byte[] bytes = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11};
InputStream is = new ByteArrayInputStream(bytes);
int len = 4;
int readCnt = 0;
int offset = 0;
byte[] buffer = new byte[1024];
while ((readCnt = is.read(buffer, offset, len)) != -1) {
offset += readCnt;
for (int index = 0; index < offset; index++) {
System.out.print(buffer[index]);
}
System.out.println();
}
}
입력 스트림에서 buffer[off] 부터 len개 만큼 buffer에 저장합니다. read 메소드는 읽은 데이터의 개수를 반환하고 더 이상 읽을 데이터가 없다면 -1을 반환합니다. 만약 len개를 읽지 못하면 실제로 읽은 데이터 수를 반환합니다.
위 소스는 데이터를 4개씩 읽으며, 다시 읽을 때 배열의 처음 위치가 아닌 offset 위치 즉, buffer[offset] 부터 읽습니다. offset은 읽은 데이터 수(readCnt) 만큼 증가합니다. 간혹 len만큼 증가하게 하는 경우가 있는데, 읽을 데이터의 수가 꼭 len 값으로 나누어 떨어지지 않기 때문에 offset은 읽은 데이터의 수(readCnt)만큼 증가 해주어야합니다. 보통 네트워크 통신할 때 많이 사용합니다.
출력 ------------ 1234 12345678 1234567891011
public long skip(long n) throws IOException
읽을 데이터 중 n 바이트를 스킵하고 실제로 스킵한 바이크 개수가 반환됩니다.
int available() throws IOException
읽을 데이터(바이트)가 얼마나 남아있는지 반환됩니다.
public synchronized void mark(int readlimit) {}
되돌아갈 특정 위치를 마킹하는 메소드 입니다. readlimit은 현재 위치를 마킹하고 최대 몇개의 byte를 더 읽을 수 있는지를 의미합니다.
public synchronized void reset() throws IOException {
throw new IOException("mark/reset not supported");
}
마킹한 지점으로 되돌아가는 메소드입니다. 기본적으로 지원되지 않으며, InputStream 추상클래스를 상속받은 클래스에서 오버라이딩해주어야 사용가능합니다.
public boolean markSupported() {
return false;
}
mark 메소드의 지원 유무를 확인하는 메소드입니다. 기본적으로 지원하지 않으므로 false를 반환합니다.
3. OutputStream
바이트 기반 출력 스트림의 최상위 추상클래스입니다. 모든 바이트 기반 출력 스트림 클래스는 이 클래스를 상속 받아 기능을 재정의 합니다.
4. OutputStream의 기본 메소드
public abstract void write(int b) throws IOException;
public void write(byte b[]) throws IOException {
write(b, 0, b.length);
}
public void write01() throws IOException {
byte[] bytes = {9, 8, 7, 6, 5, 4, 3, 2, 1, 0};
File file = new File("D://write_test.txt");
OutputStream outputStream = new FileOutputStream(file);
for (byte b : bytes) {
outputStream.write(b);
}
//바이트를 한번에 넣을 수 있다.
//outputStream.write(bytes);
outputStream.close();
}
바이트 배열을 write_text.txt 파일에 write하는 예제입니다. OutputStream을 상속받은 FileOutputStream을 생성하고, 한 바이트 씩 write하거나 여러 바이트씩 write 할 수 있습니다.
public void write(byte b[], int off, int len) throws IOException
public void write() throws IOException {
byte[] bytes = {9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 7, 8, 7, 8, 5, 2, 4};
int len = 3;
int offset = 0;
int total = bytes.length;
File file = new File("D://write_test.txt");
OutputStream outputStream = new FileOutputStream(file);
while (total >= (offset + len)) {
outputStream.write(bytes, offset, len);
offset += len;
}
if (total - offset > 0) {
outputStream.write(bytes, offset, total - offset);
}
outputStream.close();
}
byte 배열 b를 offset부터 len만큼 write합니다.
public void read() throws IOException {
File file = new File("D://write_test.txt");
InputStream inputStream = new FileInputStream(file);
int b;
while ((b = inputStream.read()) != -1) {
System.out.print(b);
}
}
public class Main {
public static void main(String[] args) {
//배열을 초기화하는 3가지 방법
int[] arr1 = new int[10];
int[] arr2 = {1, 3, 4, 5, 6};
int[] arr3 = new int[]{1, 3, 2, 4, 1, 5, 1};
//특정 인덱스에 데이터 삽입
arr3[0] = 500;
//배열을 출력하는 방법(향상된 for문)
for (int n : arr3) {
System.out.println(n);
}
//일반적인 배열 출력
for (int index = 0; index < arr3.length; index++) {
System.out.println(arr3[index]);
}
}
}
람다 함수는 함수형 프로그래밍 언어에서 사용되는 개념으로 익명 함수라고도 한다. Java 8 부터 지원되며, 불필요한 코드를 줄이고 가독성을 향상시키는 것을 목적으로 두고있다.
2. 람다 함수의 특징
메소드의 매개변수로 전달될 수 있고, 변수에 저장될 수 있다. 즉, 어떤 전달되는 매개변수에 따라서 행위가 결정될 수 있음을 의미한다.
컴파일러 추론에 의지하고 추론이 가능한 코드는 모두 제거해 코드를 간결하게 한다.
3. 람다식 표현
파라미터와 몸체로 구분된다.
파라미터와 몸체 사이에 -> 구분을 추가하여 람다식을 완성시킨다.
몸체 부분이 단일 행일 경우 중괄호와 return문을 생략할 수 있다.
( 파라미터 ) -> { 몸체 }
4. 익명함수를 람다식으로 변경하기
기존 방법
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("Thread!");
}
}).start();
람다식
new Thread(() -> {
System.out.println("Thread!");
}).start();
기존의 방식은 Thread 사용 시 Runnable 인터페이스를 익명함수로 구현하였지만, Java 1.8부터 람다 함수을 통해 구현할 수 있게 되었다.
람다 함수를 매개변수로 넘기기 위해서는 메소드의 매개변수가 @FunctionalInterface로 선언된 인터페이스여야한다. @FunctionalInterface이 선언된 인터페이스를 함수 인터페이스라 불리는데, 조건으로 반드시 추상 메소드 한개만 정의되어 있어야 한다.
그러면 람다식을 매개변수로 저장한 Runnable 인터페이스는 함수형 인터페이스일까?
@FunctionalInterface
public interface Runnable {
/**
* When an object implementing interface <code>Runnable</code> is used
* to create a thread, starting the thread causes the object's
* <code>run</code> method to be called in that separately executing
* thread.
* <p>
* The general contract of the method <code>run</code> is that it may
* take any action whatsoever.
*
* @see java.lang.Thread#run()
*/
public abstract void run();
}
Runnable 인터페이스를 보면 @FunctionalInterface와 한개의 추상메소드가 선언되어 있다. 즉, 함수형 인터페이스 조건에 성립하는 것이다.
5. 함수형 인터페이스
함수형 인터페이스의 조건
@FunctionalInterface 어노테이션을 선언해야한다.
추상 메소드가 한개만 선언되어야한다.
interface로 선언되어야한다.
이렇게 람다식을 위한 함수형 인터페이스 선언 조건에 대해서 알아보았다. 하지만 Java의 함수형 프로그래밍에는 큰 단점이 있다. 그것은 함수형 인터페이스 안에 선언된 메소드에 종속되는 람다식 밖에 구현할 수 없다는 점이다. 그래서 매개변수의 타입과 개수, 반환 값의 유무 등을 가진 메소드를 하나의 함수형 인터페이스로 구현할 수 없고, 필요한 동작에 따라서 함수형 인터페이스를 만들어줘야한다.
하지만 자바에서 우리가 사용할만한 함수형 인터페이스를 미리 정의해두었다.
Suplier<T>
Consumer<T>
Function<T,R>
Predicate<T>
1). Suplier<T>
매개 변수는 없고, 반환 값이 있는 함수형 인터페이스이다. 추상 메소드 T supplier()를 가진다.
위에 선언된 consumer는 void accept(String s) 메소드를 참조할 수 있다 그리고 System.out.println()는 void println(String str)로 선언된 메소드이다. 그래서 consumer는 println 메소드를 :: 연산자를 통해 참조 가능하다.
Java는 가전제품 내에서 동작하는 임베디드 프로그램을 위한 언어로 썬 마이크로시스템즈사의 제임스 고슬링 팀에 의해 개발 되었습니다.
1991년에 Oak라는 이름으로 시작하여, 1996년에 발표된 1.0.2 버전부터 Java라는 이름을 사용하게 됩니다.
1998년 발표된 J2SE 1.2에서는 웹에서 Java를 실행시킬 수 있게 해주는 Java 애플릿이 추가되면서 웹의 인기와 함께 Java의 인기도 급상승 하게 됩니다. 다양한 기능이 지원되면서 꾸준한 인기를 누리다가 2009년 썬 마이크로시스템즈사가 오라클과 인수 합병되면서 Java의 소유권은 오라클로 넘어갑니다.
2. Java의 특징
1. JVM을 이용해서 운영체제와 상관없이 독립적으로 실행할 수 있습니다. 즉 운영체제 별로 Java프로그램을 개발할 필요가 없습니다.
2. 가비지 컬렉터라는 메모리 관리 프로그램을 지원합니다.
3. 객체 지향 언어로서 클래스 계층 구조, 상속성, 다형성, 캡슐화를 지원합니다.
4. 멀티 스레드를 지원하여 하나의 프로그램에서 다수의 스레드가 동시에 실행될 수 있습니다.
3. Java 컴파일 및 실행 과정
4. JDK 란
JDK (Java Developement Kit) Java 프로그램을 개발하기 위한 도구들로 이루어진 패키지입니다.
JRE (Java Runtime Environment) Java 프로그램을 개발하는 데 사용되는 라이브러리들의 모음이며, Runtime 환경을 제공하는 데 쓰입니다. (JRE는 JVM과 Libraries로 구성되어 있습니다.)
JVM (Java Virtual Machine) 바이트 코드를 실행 시킬 수 있는 가상 머신입니다. 그리고 JVM은 운영체제 별로 존재하는데, 운영체제에 맞는 JVM이 설치되어 있다면 어떠한 바이트 코드라도 실행시킬 수 있습니다.
즉, 윈도우즈에서 개발된 Java 프로그램이 CentOS의 JVM 위에서 동작될 수 있다는 의미입니다.
5. Java 환경 변수란?
리눅스나 윈도우즈에서 JDK를 설치하면 Java 환경변수를 지정해주어야 합니다. 그렇다면 왜 Java 환경 변수를 등록해주어야 할까요?
JDK를 설치하게 되면 임의의 경로에 저장을 합니다. 하지만 운영체제는 설치한 JDK의 위치를 알지 못합니다. 그래서 사용하는 것이 환경 변수입니다. 환경 변수는 운영체제가 참조하는 변수입니다. 환경 변수에 JDK의 경로를 등록하게 되면 JDK의 여러 바이너리 파일들을 사용할 수 있습니다.
Windows 내컴퓨터 > 시스템 속성 > 환경 변수 > 시스템 변수 > Path에 추가 C:\Program Files\Java\jdk1.8.0_191\bin;