해시 테이블(hash table), 해시 맵(hash map), 해시 표는 컴퓨팅에서 키를 값에 매핑할 수 있는 구조인, 연관 배열 추가에 사용되는 자료 구조이다. 해시 테이블은 해시 함수를 사용하여 색인(index)을 버킷(bucket)이나 슬롯(slot)의 배열로 계산한다.
삽입
하거나 저장된 데이터를 가져올 때
주로 사용하는 기법이다. 해싱은 해시 테이블
이라는 자료구조를 이용한다. 해싱을 이용하면 데이터를 빠르게 삽입하고, 삭제하고, 가져올 수 있지만
, 최솟값이나 최댓값 찾기 등 검색 동작은 효율이 떨어진다
. 따라서 검색이 필요한 상황이라면 이진 탐색 트리 같은 자료구조를 사용하는 것이 좋다.key
라 불리는 데이터 요소로 배열에 저장된 데이터 요소를 참조할 수 있다.충돌(collision)
이라 한다.소수
여야 한다.소수
로 설정하는 이유도 이와 관련이 있다.모듈러(modular) 해싱
이라 한다.function HashTable() {
this.table = new Array(137);
this.simpleHash = simpleHash;
this.showDistro = showDistro;
this.put = put;
//this.get = get;
}
function simpleHash(data) {
let total = 0;
for (let i = 0; i < data.length; ++i) {
total += data.charCodeAt(i);
}
console.log('Hash value: ' + data + '->' + total);
return total % this.table.length;
}
function put(data) {
let pos = this.simpleHash(data);
this.table[pos] = data;
}
function showDistro() {
let n = 0;
for (let i = 0; i < this.table.length; ++i) {
if (this.table[i] != undefined) {
console.log(i + ':' + this.table[i]);
}
}
}
let someNames = [
'Raymond',
'Clayton',
'Marco',
'Poky',
'Jisoo',
'Jinnie',
'Millie',
'Dotori',
];
let hTable = new HashTable();
for (let i = 0; i < someNames.length; ++i) {
hTable.put(someNames[i]);
}
hTable.showDistro();
//output
Hash value: Raymond->730
Hash value: Clayton->730
Hash value: Marco->498
Hash value: Poky->419
Hash value: Jisoo->516
Hash value: Jinnie->605
Hash value: Millie->604
Hash value: Dotori->625
8:Poky
45:Clayton
56:Millie
57:Jinnie
77:Dotori
87:Marco
105:Jisoo
소수
여야 한다.모듈로 연산
을 사용하기 때문이다. 테이블에서 키가 균등하게 분포하도록 만들려면 배열의 크기가 100이상이어야 한다. 100보다 작은 소수를 선택하면 충돌이 더 잦아진다.호너의 메소드
라는 알고리즘을 이용하여 문자열의 아스키 값을 더하는 기법을 개선할 수 있다.호너의 메소드
를 이용하려면 결과에 소수를 곱하는 과정을 추가해야 한다.function HashTable() {
this.table = new Array(137);
this.simpleHash = simpleHash;
this.betterHash = betterHash;
this.showDistro = showDistro;
this.put = put;
//this.get = get;
}
//충돌을 피하지 못한 단순한 해시 함수
function simpleHash(data) {
let total = 0;
for (let i = 0; i < data.length; ++i) {
total += data.charCodeAt(i);
}
console.log('Hash value: ' + data + '->' + total);
return total % this.table.length;
}
//호너의 메소드를 적용하여 개선된 해시 함수
function betterHash(string) {
const H = 37; //소수
let total = 0;
for (let i = 0; i < string.length; ++i) {
total += H * total + string.charCodeAt(i); //결과에 소수를 곱하는 과정 추가
}
console.log('Hash value: ' + string + '->' + total);
total = total % this.table.length;
return parseInt(total, 10);
}
function put(data) {
let pos = this.betterHash(data);
this.table[pos] = data;
}
function showDistro() {
let n = 0;
for (let i = 0; i < this.table.length; ++i) {
if (this.table[i] != undefined) {
console.log(i + ':' + this.table[i]);
}
}
}
let someNames = [
'Raymond',
'Clayton',
'Marco',
'Poky',
'Jisoo',
'Jinnie',
'Millie',
'Dotori',
];
let hTable = new HashTable();
for (let i = 0; i < someNames.length; ++i) {
hTable.put(someNames[i]);
}
hTable.showDistro();
//output
Hash value: Raymond->254841041852
Hash value: Clayton->210499205408
Hash value: Marco->166046545
Hash value: Poky->4554231
Hash value: Jisoo->160232013
Hash value: Jinnie->6088540563
Hash value: Millie->6326133435
Hash value: Dotori->5625971393
22:Raymond
58:Clayton
63:Millie
77:Poky
79:Marco
85:Dotori
101:Jisoo
126:Jinnie
function HashTable() {
this.table = new Array(137);
this.simpleHash = simpleHash;
this.betterHash = betterHash;
this.showDistro = showDistro;
this.put = put;
//this.get = get;
}
// HashTable 가져온다.
function getRandomInt(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
function getStuData(arr) {
for (let i = 0; i < arr.length; ++i) {
let num = '';
for (let j = 0; j <= 9; ++j) {
num += Math.floor(Math.random() * 10);
}
num += getRandomInt(50, 100);
arr[i] = num;
}
}
const numStudents = 10;
const arrSize = 97;
const idLen = 9;
const students = new Array(numStudents);
getStuData(students);
for (let i = 0; i < students.length; i++) {
console.log(students[i].substring(0, 8) + ' ' + students[i].substring(9));
}
const 정수테이블 = new HashTable();
for (let i = 0; i < students.length; i++) {
정수테이블.put(students[i]);
}
정수테이블.showDistro();
function HashTable() {
this.table = new Array(137);
this.betterHash = betterHash;
this.showDistro = showDistro;
this.put = put;
this.get = get;
}
//호너의 메소드를 적용하여 개선된 해시 함수
function betterHash(string) {
const H = 117; //소수
let total = 0;
for (let i = 0; i < string.length; ++i) {
total += H * total + string.charCodeAt(i); //결과에 소수를 곱하는 과정 추가
}
console.log('Hash value: ' + string + '->' + total);
total = total % this.table.length;
return parseInt(total, 10);
}
// put() 함수는 키를 해시한 다음 해시 함수의 계산 결과로 나온 위치를 이용해 테이블에 데이터를 저장한다.
function put(key, data) {
this.table[this.betterHash(key)] = data;
}
// get() 함수는 다시 키를 해시해서 데이터가 어느 위치에 저장되어 있는지 계산한 다음, 테이블에서 데이터를 가져와야 한다.
function get(key) {
return this.table[this.betterHash(key)];
}
function showDistro() {
let n = 0;
for (let i = 0; i < this.table.length; ++i) {
if (this.table[i] != undefined) {
console.log(i + ':' + this.table[i]);
}
}
}
const hTable = new HashTable();
hTable.put('대한민국', '서울');
hTable.put('미국', '워싱턴');
hTable.put('일본', '도쿄');
console.log(hTable.get('미국'));
hTable.showDistro();
//output
Hash value: 대한민국->76056550277
Hash value: 미국->5722557
Hash value: 일본->6074400
Hash value: 미국->5722557
워싱턴
40:서울
67:워싱턴
94:도쿄
분리된 체인
, 선형 조사
라는 두 가지 충돌 해결 방법을 확인한다.분리된 체인 기법
에서는 두 키의 해시 결과가 같을 때 각 키는 두 번째 배열의 서로 다른 장소에 저장된다.이차원 배열
이 생성된다.//위 코드에서 수정,추가된 부분
function showDistro() {
let n = 0;
for (let i = 0; i < this.table.length; ++i) {
if (this.table[i][0] != undefined) {
console.log(i + ':' + this.table[i]);
}
}
}
//두 번재 배열(체인)을 만드는 buildChains 함수
hTable.buildChains();
function buildChains() {
for (let i = 0; i < this.table.length; ++i) {
this.table[i] = new Array();
}
}
오픈 주소법 해싱(open-addressing hashing)
이라 불리는 일반적인 해싱 기법이다.//put과 get 변경
class HashTable {
constructor(settingSize) {
this.table = new Array(settingSize);
this.values = [];
}
}
HashTable.prototype.betterHash = function (string) {
const H = 117;
let total = 0;
for (let i = 0; i < string.length; ++i) {
total += H * total + string.charCodeAt(i);
}
console.log('Hash value: ' + string + '->' + total);
total = total % this.table.length;
return parseInt(total, 10);
};
HashTable.prototype.showDistro = function () {
for (let i = 0; i < this.table.length; ++i) {
if (this.table[i] != undefined) {
console.log(i + ':' + this.table[i]);
}
}
};
HashTable.prototype.put = function (key, data) {
let pos = this.betterHash(key);
if (this.table[pos] == undefined) {
this.table[pos] = key;
this.values[pos] = data;
} else {
while (this.table[pos] != undefined) {
pos++;
}
this.table[pos] = key;
this.values[pos] = data;
}
};
HashTable.prototype.get = function (key) {
let hash = -1;
hash = this.betterHash(key);
if (hash > -1) {
for (let i = hash; this.table[hash] != undefined; i++) {
if (this.table[hash] == key) {
return this.values[hash];
}
}
}
return undefined;
};
const hTable = new HashTable(2);
hTable.put('대한민국', '서울');
hTable.put('미국', '워싱턴');
hTable.put('일본', '도쿄');
hTable.put('영국', '런던');
hTable.put('중국', '베이징');
console.log(hTable.get('미국'));
hTable.showDistro();
console.log(hTable);
객체(object)의 집합
으로 프로그램을 표현하려는 프로그래밍 패러다임을 말한다.//생성자 함수
function Circle(radius) {
// 생성자 함수 호출 → this바인딩 : 생성자 함수가 미래에 생성할 인스턴스
this.radius = radius;
//Circle 생성자 함수가 생성한 모든 인스턴스가 getArea 메서드를 공유해서 사용할 수 있도록 프로토타입에 추가
Circle.prototype.getArea = function () {
//this.getArea 가 아니라 Circle.prototype.getArea
return Math.PI * this.radius ** 2;
};
}
const circle1 = new Circle(1);
const circle2 = new Circle(2);
//Circle 생성자 함수가 생성하는 모든 인스터스는 하나의 getArea 메서드를 공유한다.
console.log(circle1.getArea === circle2.getArea); //true
console.log(circle1.getArea()); //3.141592653589793
console.log(circle2.getArea()); //12.566370614359172
key in object;
//ES6에서는 Refelect.has 메서드 사용 가능
Reflect.has(object, key);
const myAge = { age: 100 };
const person = {
name: 'Marco',
address: 'Seoul',
__proto__: myAge,
};
for (const key in person) {
// 객체 자신의 프로퍼티인지 확인한다.
if (!person.hasOwnProperty(key)) continue;
console.log(key + ': ' + person[key]);
}
//"name: Marco"
//"address: Seoul"
자신이 속한 객체
또는 자신이 생성할 인스턴스
를 가리키는 자기 참조 변수다. this를 통해 자신이 속한 객체 또는 자신이 생성할 인스턴스의 프로퍼티나 메서드를 참조
할 수 있다. this는 자바스크립트 엔진에 의해 암묵적으로 생성되며, 코드 어디서든 호출
할 수 있다.this가 가리킬 객체
를 바인딩하는 것이다.함수 호출 방식
에 의해 동적
으로 결정된다. this바인딩은 함수 호출 시점에 결정된다.메서드의 this 바인딩
과 일치시키기 위한 방법은 다음과 같다.function getThisBinding() {
return this;
}
const thisArg = { a: 1 };
console.log(getThisBinding.bind(thisArg)); //function () { [native code] }
//함수를 호출하지는 않으므로 명시적으로 호출해야 한다.
console.log(getThisBinding.bind(thisArg)()); //[object Object] { a: 1}
const person = {
name: 'Marco',
foo(callback) {
//bind메서드로 callback 함수 내부의 this 바인딩을 전달
setTimeout(callback.bind(this), 100);
},
};
person.foo(function () {
//위에서 bind 메서드를 사용하지 않았다면 일반 함수로 호출된 콜백 함수 내부의 this.name은 브라우저 환경에서 window.name과 같다.
console.log(`hi! my name is ${this.name}`);
});
this 바인딩 정리
//apply, call, bind 개념 정리
function a(x, y, z) {
console.log(this, x, y, z);
}
const b = {
isThis: 'thisthis',
};
a.call(b, 1, 2, 3);
// { isThis: 'thisthis' } 1 2 3
a.apply(b, [1, 2, 3]);
// { isThis: 'thisthis' } 1 2 3
const c = a.bind(b, 1, 2, 3);
c(1, 2, 3);
// { isThis: 'thisthis' } 1 2 3
const d = a.bind(b);
d(1, 2, 3);
// { isThis: 'thisthis' } 1 2 3
function getThisBinding() {
console.log(arguments);
return this;
}
const thisArg = { a: 1 };
//apply와 call의 첫번째 인수는 this로 연결할 객체이고, 두번째 인수부터는 매개변수로 전달된다.
//apply 메서드는 호출할 함수의 인수를 배열로 묶어 전달한다
console.log('1', getThisBinding.apply(thisArg, [1, 2, 3]));
//call 메서드는 호출할 함수의 인수를 쉼표로 구분한 리스트 형식으로 전달한다
console.log('2', getThisBinding.call(thisArg, 1, 2, 3));