|
| 1 | +package struct; |
| 2 | + |
| 3 | +/** |
| 4 | + * A clone of a Python dictionary in a Java class that simulates a key-value store using a hash table |
| 5 | + * implemented from scratch without using standard collections like Map. |
| 6 | + */ |
| 7 | +public class DictionarySort { |
| 8 | + private static final int SIZE = 10; // Number of buckets |
| 9 | + private Bucket[] buckets; |
| 10 | + private SortStrategy sortStrategy = SortStrategy.KEY; // Default sorting strategy |
| 11 | + |
| 12 | + /** |
| 13 | + * Enum to represent sorting strategies. |
| 14 | + */ |
| 15 | + public enum SortStrategy { |
| 16 | + KEY, VALUE |
| 17 | + } |
| 18 | + |
| 19 | + /** |
| 20 | + * Inner class to represent a key-value pair. |
| 21 | + */ |
| 22 | + private static class Entry { |
| 23 | + String key; |
| 24 | + Object value; |
| 25 | + |
| 26 | + Entry(String key, Object value) { |
| 27 | + this.key = key; |
| 28 | + this.value = value; |
| 29 | + } |
| 30 | + } |
| 31 | + |
| 32 | + /** |
| 33 | + * Inner class to represent a bucket in the hash table. |
| 34 | + */ |
| 35 | + private static class Bucket { |
| 36 | + Entry entry; |
| 37 | + Bucket next; |
| 38 | + |
| 39 | + Bucket(Entry entry) { |
| 40 | + this.entry = entry; |
| 41 | + this.next = null; |
| 42 | + } |
| 43 | + } |
| 44 | + |
| 45 | + /** |
| 46 | + * Constructs an empty dictionary. |
| 47 | + */ |
| 48 | + public DictionarySort() { |
| 49 | + this.buckets = new Bucket[SIZE]; |
| 50 | + } |
| 51 | + |
| 52 | + /** |
| 53 | + * Constructs a dictionary with initial key-value pairs. |
| 54 | + * |
| 55 | + * @param keyValuePairs An even number of arguments where each pair consists of a key and a value. |
| 56 | + * @throws IllegalArgumentException if the number of arguments is odd. |
| 57 | + */ |
| 58 | + public DictionarySort(Object... keyValuePairs) { |
| 59 | + this(); |
| 60 | + if (keyValuePairs.length % 2 != 0) { |
| 61 | + throw new IllegalArgumentException("Key-value pairs must be even."); |
| 62 | + } |
| 63 | + for (int i = 0; i < keyValuePairs.length; i += 2) { |
| 64 | + String key = (String) keyValuePairs[i]; |
| 65 | + Object value = keyValuePairs[i + 1]; |
| 66 | + put(key, value); |
| 67 | + } |
| 68 | + } |
| 69 | + |
| 70 | + /** |
| 71 | + * Computes the index in the hash table for a given key. |
| 72 | + * |
| 73 | + * @param key The key for which the index is computed. |
| 74 | + * @return The index in the hash table. |
| 75 | + */ |
| 76 | + private int getIndex(String key) { |
| 77 | + return Math.abs(key.hashCode()) % SIZE; |
| 78 | + } |
| 79 | + |
| 80 | + /** |
| 81 | + * Adds or updates a key-value pair in the dictionary. |
| 82 | + * |
| 83 | + * @param key The key to be added or updated. |
| 84 | + * @param value The value associated with the key. |
| 85 | + */ |
| 86 | + public void put(String key, Object value) { |
| 87 | + int index = getIndex(key); |
| 88 | + Bucket bucket = buckets[index]; |
| 89 | + |
| 90 | + // Traverse the bucket to find if the key already exists |
| 91 | + while (bucket != null) { |
| 92 | + if (bucket.entry.key.equals(key)) { |
| 93 | + // Update the value if the key is found |
| 94 | + bucket.entry.value = value; |
| 95 | + return; |
| 96 | + } |
| 97 | + bucket = bucket.next; |
| 98 | + } |
| 99 | + |
| 100 | + // If key is not found, add a new entry to the beginning of the bucket |
| 101 | + Bucket newBucket = new Bucket(new Entry(key, value)); |
| 102 | + newBucket.next = buckets[index]; |
| 103 | + buckets[index] = newBucket; |
| 104 | + } |
| 105 | + |
| 106 | + /** |
| 107 | + * Retrieves the value associated with a given key. |
| 108 | + * |
| 109 | + * @param key The key whose value is to be retrieved. |
| 110 | + * @return The value associated with the key, or null if the key is not found. |
| 111 | + */ |
| 112 | + public Object get(String key) { |
| 113 | + int index = getIndex(key); |
| 114 | + Bucket bucket = buckets[index]; |
| 115 | + |
| 116 | + // Traverse the bucket to find the key |
| 117 | + while (bucket != null) { |
| 118 | + if (bucket.entry.key.equals(key)) { |
| 119 | + return bucket.entry.value; |
| 120 | + } |
| 121 | + bucket = bucket.next; |
| 122 | + } |
| 123 | + return null; // Return null if the key is not found |
| 124 | + } |
| 125 | + |
| 126 | + /** |
| 127 | + * Removes the key-value pair associated with the given key. |
| 128 | + * |
| 129 | + * @param key The key to be removed. |
| 130 | + */ |
| 131 | + public void remove(String key) { |
| 132 | + int index = getIndex(key); |
| 133 | + Bucket bucket = buckets[index]; |
| 134 | + Bucket prev = null; |
| 135 | + |
| 136 | + // Traverse the bucket to find the key |
| 137 | + while (bucket != null) { |
| 138 | + if (bucket.entry.key.equals(key)) { |
| 139 | + if (prev == null) { |
| 140 | + // Removing the first bucket in the slot |
| 141 | + buckets[index] = bucket.next; |
| 142 | + } else { |
| 143 | + // Removing a bucket from the middle or end |
| 144 | + prev.next = bucket.next; |
| 145 | + } |
| 146 | + return; |
| 147 | + } |
| 148 | + prev = bucket; |
| 149 | + bucket = bucket.next; |
| 150 | + } |
| 151 | + } |
| 152 | + |
| 153 | + /** |
| 154 | + * Checks if the dictionary contains the specified key. |
| 155 | + * |
| 156 | + * @param key The key to check for. |
| 157 | + * @return true if the dictionary contains the key, false otherwise. |
| 158 | + */ |
| 159 | + public boolean containsKey(String key) { |
| 160 | + int index = getIndex(key); |
| 161 | + Bucket bucket = buckets[index]; |
| 162 | + |
| 163 | + // Traverse the bucket to find the key |
| 164 | + while (bucket != null) { |
| 165 | + if (bucket.entry.key.equals(key)) { |
| 166 | + return true; |
| 167 | + } |
| 168 | + bucket = bucket.next; |
| 169 | + } |
| 170 | + return false; |
| 171 | + } |
| 172 | + |
| 173 | + /** |
| 174 | + * Sets the sorting strategy for the dictionary. |
| 175 | + * |
| 176 | + * @param strategy The sorting strategy to use. |
| 177 | + */ |
| 178 | + public void setSortStrategy(SortStrategy strategy) { |
| 179 | + this.sortStrategy = strategy; |
| 180 | + } |
| 181 | + |
| 182 | + /** |
| 183 | + * Returns a string representation of the dictionary in a JSON-like format without formatting. |
| 184 | + * |
| 185 | + * @return A JSON-like string representation of the dictionary. |
| 186 | + */ |
| 187 | + @Override |
| 188 | + public String toString() { |
| 189 | + return toString(false); |
| 190 | + } |
| 191 | + |
| 192 | + /** |
| 193 | + * Returns a string representation of the dictionary in a JSON-like format with optional formatting. |
| 194 | + * |
| 195 | + * @param formatted If true, returns the string in a formatted JSON-like format. |
| 196 | + * @return A JSON-like string representation of the dictionary. |
| 197 | + */ |
| 198 | + public String toString(boolean formatted) { |
| 199 | + StringBuilder sb = new StringBuilder(); |
| 200 | + sb.append(formatted ? "{\n" : "{"); |
| 201 | + |
| 202 | + Entry[] entries = getAllEntries(); |
| 203 | + if (sortStrategy == SortStrategy.KEY) { |
| 204 | + sortByKey(entries); |
| 205 | + } else if (sortStrategy == SortStrategy.VALUE) { |
| 206 | + sortByValue(entries); |
| 207 | + } |
| 208 | + |
| 209 | + boolean first = true; |
| 210 | + for (Entry entry : entries) { |
| 211 | + if (!first) { |
| 212 | + sb.append(formatted ? ",\n" : ", "); |
| 213 | + } |
| 214 | + sb.append(formatted ? " " : "") |
| 215 | + .append("\"").append(entry.key).append("\": ") |
| 216 | + .append("\"").append(entry.value.toString()).append("\""); |
| 217 | + first = false; |
| 218 | + } |
| 219 | + sb.append(formatted ? "\n}" : "}"); |
| 220 | + return sb.toString(); |
| 221 | + } |
| 222 | + |
| 223 | + /** |
| 224 | + * Retrieves all entries from the dictionary. |
| 225 | + * |
| 226 | + * @return An array of all entries. |
| 227 | + */ |
| 228 | + private Entry[] getAllEntries() { |
| 229 | + // Gather all entries from all buckets |
| 230 | + int count = 0; |
| 231 | + for (Bucket bucket : buckets) { |
| 232 | + while (bucket != null) { |
| 233 | + count++; |
| 234 | + bucket = bucket.next; |
| 235 | + } |
| 236 | + } |
| 237 | + |
| 238 | + Entry[] entries = new Entry[count]; |
| 239 | + int index = 0; |
| 240 | + for (Bucket bucket : buckets) { |
| 241 | + while (bucket != null) { |
| 242 | + entries[index++] = bucket.entry; |
| 243 | + bucket = bucket.next; |
| 244 | + } |
| 245 | + } |
| 246 | + return entries; |
| 247 | + } |
| 248 | + |
| 249 | + /** |
| 250 | + * Sorts entries by key using quicksort. |
| 251 | + * |
| 252 | + * @param entries The array of entries to be sorted. |
| 253 | + */ |
| 254 | + private void sortByKey(Entry[] entries) { |
| 255 | + quickSort(entries, 0, entries.length - 1, true); |
| 256 | + } |
| 257 | + |
| 258 | + /** |
| 259 | + * Sorts entries by value using quicksort. |
| 260 | + * |
| 261 | + * @param entries The array of entries to be sorted. |
| 262 | + */ |
| 263 | + private void sortByValue(Entry[] entries) { |
| 264 | + quickSort(entries, 0, entries.length - 1, false); |
| 265 | + } |
| 266 | + |
| 267 | + /** |
| 268 | + * Quicksort implementation to sort entries by key or value. |
| 269 | + * |
| 270 | + * @param entries The array of entries to be sorted. |
| 271 | + * @param low The starting index. |
| 272 | + * @param high The ending index. |
| 273 | + * @param byKey True to sort by key, false to sort by value. |
| 274 | + */ |
| 275 | + private void quickSort(Entry[] entries, int low, int high, boolean byKey) { |
| 276 | + if (low < high) { |
| 277 | + int pivotIndex = partition(entries, low, high, byKey); |
| 278 | + quickSort(entries, low, pivotIndex - 1, byKey); |
| 279 | + quickSort(entries, pivotIndex + 1, high, byKey); |
| 280 | + } |
| 281 | + } |
| 282 | + |
| 283 | + /** |
| 284 | + * Partition method for quicksort. |
| 285 | + * |
| 286 | + * @param entries The array of entries to be partitioned. |
| 287 | + * @param low The starting index. |
| 288 | + * @param high The ending index. |
| 289 | + * @param byKey True to sort by key, false to sort by value. |
| 290 | + * @return The index of the pivot element. |
| 291 | + */ |
| 292 | + private int partition(Entry[] entries, int low, int high, boolean byKey) { |
| 293 | + Entry pivot = entries[high]; |
| 294 | + int i = low - 1; |
| 295 | + for (int j = low; j < high; j++) { |
| 296 | + boolean condition; |
| 297 | + if (byKey) { |
| 298 | + condition = entries[j].key.compareTo(pivot.key) < 0; |
| 299 | + } else { |
| 300 | + condition = entries[j].value.toString().compareTo(pivot.value.toString()) < 0; |
| 301 | + } |
| 302 | + if (condition) { |
| 303 | + i++; |
| 304 | + Entry temp = entries[i]; |
| 305 | + entries[i] = entries[j]; |
| 306 | + entries[j] = temp; |
| 307 | + } |
| 308 | + } |
| 309 | + Entry temp = entries[i + 1]; |
| 310 | + entries[i + 1] = entries[high]; |
| 311 | + entries[high] = temp; |
| 312 | + return i + 1; |
| 313 | + } |
| 314 | +} |
0 commit comments