Skip to content

Commit

Permalink
[SS-004] HashMap (#5)
Browse files Browse the repository at this point in the history
* [SS-004] HashMap초기 구현

* [SS-004] 정렬리스트 추출 구현

* [SS-004] HashMap 삽입삭제 구현

* [SS-004] testASCAndDESCList

* [SS-004] ASC, DESC 최적화

리스트크기를 지정하여 최적화

* [SS-004] HashMap에 적용

* [SS-004] HashMap멀티쓰레드 환경 테스트 코드 추가

* [SS-004] Docs, HashMap Readme작성
  • Loading branch information
J0onYEong authored Oct 12, 2024
1 parent 8ccf48b commit 6812c41
Show file tree
Hide file tree
Showing 10 changed files with 551 additions and 3 deletions.
Binary file added .DS_Store
Binary file not shown.
40 changes: 38 additions & 2 deletions Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@
# Milestone

- [x] Red-black tree
- [x] HashMap(powerd by RBTree)
- [ ] Thread-safe dictionary
- [x] NSLock
- [ ] Actor
- [ ] HashMap(powerd by RBTree)


## Red-black tree
Expand Down Expand Up @@ -86,7 +86,7 @@ When recoloring isn’t enough to maintain the Red-Black Tree properties, restru

This operation ensures the balance of the tree while maintaining the Red-Black Tree rules for node colors and structure.

#### Example
### Example

```swift
let tree = RBTree<Int>()
Expand All @@ -97,6 +97,42 @@ try tree.append([5, 20, 15, 25])
try tree.remove(5)
```

## HashMap

The HashMap performs thread-safe operations during insertion and deletion. The sortedList function returns an array of values based on the sorted key order. When a count parameter is provided, the function limits the number of returned values, optimized to run in **O(log N) + the number of count** operations.

#### Example
```swift
let hashMap = HashMap<Int, String>()

hashMap[1] = "One"
hashMap[2] = "Two"

hashMap.remove(1)
hashMap.remove(2)
```

### sortedList method

The keys in the HashMap are internally managed by a Red-Black Tree. This structure is used to quickly sort the keys stored in the dictionary. The sorted method allows you to retrieve a list of values in ascending or descending order.
```swift
let hashMap = HashMap<Int, String>()
let testCase = (1...10).shuffled()

testCase.forEach { (element) in
hashMap[element] = String(element)
}

// result : ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10"]
hashMap.ascendingValues(10)

// result : ["10", "9", "8", "7", "6", "5", "4", "3", "2", "1"]
hashMap.descendingValues(10)

```
If the count exceeds the number of key-value pairs in the dictionary, it will be set to the total pair count of the dictionary.


## Thread-safe dictionary

### LockedDictionary
Expand Down
Binary file added SwiftStructures/.DS_Store
Binary file not shown.
16 changes: 16 additions & 0 deletions SwiftStructures/SwiftStructures.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
9D667BC62CB7EA15002908C3 /* RBTreeError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9D667BC52CB7EA15002908C3 /* RBTreeError.swift */; };
9D667BCA2CB95D33002908C3 /* LockedDictionary.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9D667BC92CB95D33002908C3 /* LockedDictionary.swift */; };
9D667BCC2CB96062002908C3 /* ThreadSafeDictionaryTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9D667BCB2CB96062002908C3 /* ThreadSafeDictionaryTests.swift */; };
9D667BD62CBACE27002908C3 /* HashMap.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9D667BD52CBACE27002908C3 /* HashMap.swift */; };
9D667BD82CBADA3A002908C3 /* HashMapTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9D667BD72CBADA3A002908C3 /* HashMapTests.swift */; };
/* End PBXBuildFile section */

/* Begin PBXContainerItemProxy section */
Expand All @@ -37,6 +39,8 @@
9D667BC52CB7EA15002908C3 /* RBTreeError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RBTreeError.swift; sourceTree = "<group>"; };
9D667BC92CB95D33002908C3 /* LockedDictionary.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LockedDictionary.swift; sourceTree = "<group>"; };
9D667BCB2CB96062002908C3 /* ThreadSafeDictionaryTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThreadSafeDictionaryTests.swift; sourceTree = "<group>"; };
9D667BD52CBACE27002908C3 /* HashMap.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HashMap.swift; sourceTree = "<group>"; };
9D667BD72CBADA3A002908C3 /* HashMapTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HashMapTests.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */

/* Begin PBXFrameworksBuildPhase section */
Expand Down Expand Up @@ -79,6 +83,7 @@
9D667BA92CB7DD35002908C3 /* SwiftStructures */ = {
isa = PBXGroup;
children = (
9D667BD42CBACE11002908C3 /* HashMap */,
9D667BC82CB95CDF002908C3 /* ThreadSafeDictionary */,
9D667BBF2CB7DD87002908C3 /* RedblackTree */,
9D667BAA2CB7DD35002908C3 /* SwiftStructures.h */,
Expand All @@ -91,6 +96,7 @@
children = (
9D667BB42CB7DD35002908C3 /* RBTreeTests.swift */,
9D667BCB2CB96062002908C3 /* ThreadSafeDictionaryTests.swift */,
9D667BD72CBADA3A002908C3 /* HashMapTests.swift */,
);
path = SwiftStructuresTests;
sourceTree = "<group>";
Expand Down Expand Up @@ -121,6 +127,14 @@
path = ThreadSafeDictionary;
sourceTree = "<group>";
};
9D667BD42CBACE11002908C3 /* HashMap */ = {
isa = PBXGroup;
children = (
9D667BD52CBACE27002908C3 /* HashMap.swift */,
);
path = HashMap;
sourceTree = "<group>";
};
/* End PBXGroup section */

/* Begin PBXHeadersBuildPhase section */
Expand Down Expand Up @@ -235,6 +249,7 @@
9D667BC12CB7E2BD002908C3 /* RBTree.swift in Sources */,
9D667BCA2CB95D33002908C3 /* LockedDictionary.swift in Sources */,
9D667BC32CB7E2FF002908C3 /* RBTreeNode.swift in Sources */,
9D667BD62CBACE27002908C3 /* HashMap.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand All @@ -244,6 +259,7 @@
files = (
9D667BCC2CB96062002908C3 /* ThreadSafeDictionaryTests.swift in Sources */,
9D667BB52CB7DD35002908C3 /* RBTreeTests.swift in Sources */,
9D667BD82CBADA3A002908C3 /* HashMapTests.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down
Binary file not shown.
Binary file added SwiftStructures/SwiftStructures/.DS_Store
Binary file not shown.
95 changes: 95 additions & 0 deletions SwiftStructures/SwiftStructures/HashMap/HashMap.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
//
// HashMap.swift
// SwiftStructures
//
// Created by choijunios on 10/13/24.
//

import Foundation

/// Thread safe hash map
public class HashMap<Key, Value> where Key: Hashable & Comparable {

typealias KeyStore = RBTree<Key>

private var dictionary: [Key: Value] = .init()
private let keyStore: KeyStore = .init()

private let lock: NSLock = .init()

public subscript(key: Key) -> Value? {
get {
defer {
lock.unlock()
}
lock.lock()

return dictionary[key]
}
set(newValue) {
defer {
lock.unlock()
}
lock.lock()

if dictionary[key] == nil {
// key isn't exists in dictionary
try! keyStore.append(key)
}

dictionary[key] = newValue
}
}

public func remove(_ key: Key) {
defer {
lock.unlock()
}
lock.lock()

do {
try keyStore.remove(key)
} catch {
print("\(HashMap.self): " + error.localizedDescription)
}

dictionary.removeValue(forKey: key)
}
}


// MARK: sorted value list
public extension HashMap {

func ascendingValues(_ count: Int) -> [Value] {
defer {
lock.unlock()
}
lock.lock()

let pairCount = dictionary.count
let keyCount = count > pairCount ? pairCount : count

let keys = keyStore.sortedList(type: .ASC, count: keyCount)

return keys.compactMap { key in
dictionary[key]
}
}

func descendingValues(_ count: Int) -> [Value] {
defer {
lock.unlock()
}
lock.lock()

let pairCount = dictionary.count
let keyCount = count > pairCount ? pairCount : count

let keys = keyStore.sortedList(type: .DESC, count: keyCount)

return keys.compactMap { key in
dictionary[key]
}
}
}
Loading

0 comments on commit 6812c41

Please sign in to comment.