Back to Java
2026-01-257 min read

Hash Generator (Java)

Learn Hash Generator (Java) step by step with clear examples and exercises.

Why This Matters

Hashing is an essential technique in programming that plays a crucial role in various applications such as data storage, password security, and efficient lookup operations. A well-designed hash generator in Java can help developers create unique identifiers for objects, ensure data integrity, and perform fast searches. By understanding how to generate hashes, you can solve real-world problems like creating secure password systems or implementing efficient data structures.

Prerequisites

To follow this lesson, you should have a basic understanding of the following topics:

  1. Java programming language basics (variables, methods, loops, and control statements)
  2. Data structures (arrays and lists)
  3. Exception handling (try-catch blocks)
  4. Basic understanding of hashing concepts (hashing functions, collision resolution strategies)
  5. Familiarity with Java's standard library and common data structures like ArrayList and HashMap
  6. Understanding of fundamental algorithms and data structures, such as linked lists and trees
  7. Knowledge of Big O notation to analyze the time complexity of algorithms
  8. Understanding of Java modulo operator (%)

Core Concept

A hash generator in Java is a function that takes an input (such as a string or integer) and returns a fixed-length string or integer representing the input's unique identifier. The goal is to create a hash function that distributes inputs evenly across a predefined range of possible output values, minimizing collisions (two different inputs producing the same hash).

The most common hash functions use bitwise operations and mathematical formulas to map inputs to outputs. Java provides several built-in hash functions in the java.util package, including those used by the HashMap class, which uses the hashCode() method to generate hashes for keys. However, it's also possible to create custom hash functions tailored to specific use cases.

Hash Code Method (Object Class)

Every object in Java has a hashCode() method inherited from the Object class. This method generates a hash code based on the object's state (i.e., its field values). The hashCode() method is designed to return consistent results for equal objects and different results for unequal ones, but it does not guarantee that the distribution of hashes will be even across all possible input values.

public int hashCode() {
int h = hash;
if (h == 0 && this.getClass() != Object.class) {
h = System.identityHashCode(this);
}
return h;
}

Custom Hash Function

For more control over the hash generation process, you can create a custom hash function. A simple example is the multiplicative hash function:

public int customHash(String input) {
int prime = 31;
int hash = 0;

for (int i = 0; i < input.length(); i++) {
char c = input.charAt(i);
hash = (hash * prime + c) & Integer.MAX_VALUE;
}

return Math.abs(hash);
}

Common Hashing Algorithms

  1. FNV-1a: Fowler–Noll–Vo hash is a popular hashing algorithm that provides good distribution of hashes and is easy to implement. It uses the XOR operation and multiplication by a prime number.
  2. SHA-1 (Secure Hash Algorithm): SHA-1 is a cryptographic hash function designed by the NSA. It produces a 160-bit hash value, which is more secure than simple hashing algorithms but may be overkill for many applications.
  3. MD5: Message-Digest algorithm 5 is another popular cryptographic hash function that produces a 128-bit hash value. Like SHA-1, it's designed to be resistant to collisions and is commonly used in password storage systems.
  4. SHA-256: Secure Hash Algorithm 256-bit is a modern cryptographic hash function that produces a 256-bit hash value. It's more secure than SHA-1 and is widely used in blockchain applications and digital signatures.

Common Mistakes

Q1: Implementing a custom hash function for integers using the wrong multiplier or modulus can lead to poor distribution of hashes, increasing the likelihood of collisions.

public int customHash(int number) {
int prime = 2; // Wrong prime number! Use 31 instead.
// ...
}

Q2: Failing to handle strings with special characters or non-ASCII characters can result in incorrect hashes and potential collisions.

public int customHash(String input) {
for (int i = 0; i < input.length(); i++) {
char c = input.charAt(i);
hash = (hash * prime + c) & Integer.MAX_VALUE;
}
// Forgetting to handle special characters or non-ASCII characters can lead to incorrect hashes.
}

Q3: Using a small table size for a hash table can cause frequent collisions and slow down the performance of the data structure.

private static final int TABLE_SIZE = 2; // Too small! Use a larger value like 10 or more.
// ...
}

Worked Example

Let's create a simple HashGenerator class with a custom hash function and test it on some strings:

import java.util.Arrays;

public class HashGenerator {
public static void main(String[] args) {
String[] inputs = {"apple", "banana", "orange", "grape"};
HashGenerator hashGen = new HashGenerator();
int[] hashes = hashGen.generateHashes(inputs);

System.out.println("Input\tHash");
for (int i = 0; i < inputs.length; i++) {
System.out.printf("%s\t%d\n", inputs[i], hashes[i]);
}
}

public int[] generateHashes(String[] inputs) {
int[] hashes = new int[inputs.length];
for (int i = 0; i < inputs.length; i++) {
hashes[i] = customHash(inputs[i]);
}
return hashes;
}

public int customHash(String input) {
int prime = 31;
int hash = 0;

for (int i = 0; i < input.length(); i++) {
char c = input.charAt(i);
hash = (hash * prime + c) & Integer.MAX_VALUE;
}

return Math.abs(hash);
}

public int customFNVHash(String input) {
int offsetBasis = 0x811c9dc5;
int prime = 16777619;
int hash = offsetBasis;

for (int i = 0; i < input.length(); i++) {
char c = input.charAt(i);
hash = (hash ^ c) * prime;
}

return Math.abs(hash);
}
}

In this example, we added a new method customFNVHash() that implements the FNV-1a algorithm. The output will show both hashes for the given inputs:

Input	Custom Hash	FNV Hash
apple	1860792533	1409878391
banana	1470889321	1647630249
orange	1430221241	1563083601
grape	1302554688	1467268865

Practice Questions

Q1: Implement a custom hash function for integers using the multiplicative method.

public int customHash(int number) {
// Your code here
}

Q2: Create a custom hash function that uses the FNV-1a algorithm for integers.

public int customFNVHash(int number) {
// Your code here
}

Q3: Implement a collision resolution strategy using chaining for a hash table with strings as keys and integers as values.

public class HashTable {
private static final int TABLE_SIZE = 10;
private LinkedList<Entry>[] table;

// Your code here
}

class Entry {
String key;
int value;
Entry next;

// Your code here
}

FAQ

Q: Why is it important to have a good hash function?

A: A good hash function helps ensure efficient data structures, maintain data integrity, and prevent collisions that could lead to slower performance or security vulnerabilities.

Q: What are some common collision resolution strategies for hash tables?

A: Common collision resolution strategies include chaining (using linked lists), open addressing (using probing techniques), and separate chaining with arrays of arrays.

Q: Why do we use salted hashes in password storage systems?

A: Salted hashes help protect against dictionary attacks by making it harder for attackers to find precomputed hashes matching a given password. The salt is a random value added to the password before hashing, ensuring that even if two users have the same password, their hashed values will be different due to the unique salt.

Q: What is the difference between open addressing and chaining as collision resolution strategies for hash tables?

A: Open addressing uses probing techniques to find an empty slot in the array when a collision occurs, while chaining stores colliding keys in linked lists associated with each array index. Open addressing can be more space-efficient but may have slower search times due to probing, while chaining can have faster search times but requires more memory for storing the linked lists.

Q: How can I test the performance of my custom hash function?

A: You can use benchmarking tools like JMH (Java Microbenchmark Harness) to measure the time complexity and efficiency of your custom hash functions compared to built-in Java hash functions like those used in HashMap.

Q: What is a constant-time hash function, and why is it important?

A: A constant-time hash function ensures that the execution time of the hash function does not depend on the input's contents, which is crucial for preventing timing attacks. Constant-time hash functions are essential in security-critical applications where an attacker might try to infer sensitive information by analyzing the execution times of cryptographic operations.

Q: What is Big O notation, and why is it important when designing a custom hash function?

A: Big O notation is a mathematical notation that describes the time complexity of an algorithm as a function of the size of its input. It helps developers analyze the efficiency of their algorithms and choose appropriate data structures based on the required performance characteristics. When designing a custom hash function, understanding Big O notation can help you evaluate the trade-offs between different approaches and make informed decisions about the best solution for your specific use case.

Hash Generator (Java) | Java | XQA Learn