String Handling In Java
Java provides three primary classes for handling and manipulating strings: String, StringBuilder, and StringBuffer. Each of these classes has its own characteristics, use cases, and performance implications. In this course, we will explore these classes in depth, compare their features, and understand when to use each.
1. String in Java
A String in Java is an immutable sequence of characters. Once a String object is created, it cannot be modified. Any operation that appears to modify a String actually creates a new String object.
_1747801149981.png)
Characteristics Of String:
- Immutable: Strings are immutable, meaning their value cannot be changed after creation.
- Stored in String Pool: Java uses a special memory area called theString Poolto store string literals. This helps in memory optimization by reusing existing strings.
- Thread-Safe: Since strings are immutable, they are inherently thread-safe.
Let's analyze below image and try to understand how String internally works.

Let's try to undestand above image meaning.
There are 6 (s1, s2, s3, s4, s5, s6) strings have been assigned in Stack memory.
When we direct assign any string value that value is called literals. Literals are store inside String Pool under Heap memory.
Whenever we assign new String Literals, first it checks inside String Pool, if that content is availble in String Pool then same reference is assigned to that variable rather than creating new one.
As per above image s1, s2, s4 are string literals and s5 and s6 are Instance variable.
Instance variable are store in heap Memory and each instance variable have unique refrence area.
If two string have same content then we can check from equals() method.
Ex: if(s1.equals(s5)) // trueTo compare memory reference we use ==
Ex: if(s1 == s5) // false
Note: intern() method is used to store heaap memory refrence inside String pool then we can use intern() method.
Common Operations
Here are some common operations performed on strings:
public class StringExample {
public static void main(String[] args) {
String str1="Hello";
String str2="World";
// Concatenation
String result = str1 +" "+ str2; // "Hello World"
// Length
int length = str1.length(); // 5
// Substring
String sub = str1.substring(1, 4); // "ell"
// Character at index
char ch = str1.charAt(0); // 'H'
// Comparison
boolean isEqual = str1.equals("Hello"); // true
// Case conversion
String upper = str1.toUpperCase(); // "HELLO"
String lower = str1.toLowerCase(); // "hello"
}
}
Java String hashCode() and equals() Method Contract
In Java, the hashCode() and equals() methods are fundamental for comparing objects and for the correct functioning of hash-based collections like HashMap, HashSet, and Hashtable. The String class provides specific implementations of these methods, and they follow a strict contract defined by the Java language.
The General Contract
1. equals() Contract:
- It is reflexive: For any non-null reference value x, x.equals(x) should return true.
- It is symmetric: For any non-null reference values x and y, x.equals(y) should return true if and only if y.equals(x) returns true.
- It is transitive: For any non-null reference values x, y, and z, if x.equals(y) returns true and y.equals(z) returns true, then x.equals(z) should return true.
- It is consistent: Multiple invocations of x.equals(y) consistently return true or consistently return false, provided no information used in equals comparisons is modified.
- For any non-null reference value x, x.equals(null) should return false.
2. hashCode() Contract:
- Whenever equals() returns true for two objects, their hashCode() values must be the same.
- If two objects are not equal according to equals(), their hashCode() values may or may not be different.
- The hashCode() method must consistently return the same integer, provided no information used in equals() comparisons is modified.
String Class Implementation
- The String class overrides both equals() and hashCode() to ensure that two strings with the same sequence of characters are considered equal and have the same hash code.
equals() in String:
@Override
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
if (anObject instanceof String) {
String anotherString = (String)anObject;
int n = value.length;
if (n == anotherString.value.length) {
char v1[] = value;
char v2[] = anotherString.value;
int i=0;
while (n-- != 0) {
if (v1[i] != v2[i])
return false;
i++;
}
return true;
}
}
return false;
}
hashCode() in String:
@Override
public int hashCode() {
int h= hash;
if (h ==0 && value.length > 0) {
char val[] = value;
for (int i=0; i <value.length; i++) {
h =31* h + val[i];
}
hash = h;
}
return h;
}
Example
String s1=new String("Hello");
String s2=new String("Hello");
System.out.println(s1.equals(s2)); // true
System.out.println(s1.hashCode() == s2.hashCode()); // true
Why is this important?
- If you use String objects as keys in a HashMap, the map relies on both equals() and hashCode() to store and retrieve values correctly.
- Violating the contract can lead to unpredictable behavior in collections.
Summary Table
If a.equals(b) | Then a.hashCode() == b.hashCode() | Required? |
|---|---|---|
true | true | Yes |
false | true or false | No |
In short:
For String, if two objects are equal (equals() returns true), their hashCode() will always be the same. This is essential for the correct functioning of hash-based collections in Java.
Note: Whenever we create a custom class and try to use it in hashMap, hashSet we need to override both methods to overcome duplicate entries.
