String Data Structure
Definition and Concepts
A string is a sequence of characters used to represent text in programming. In Java, the String class is a fundamental data structure that is immutable, meaning its content cannot be changed after creation. Strings are stored as arrays of characters internally, but Java’s String class provides a high-level interface for manipulating text. You can visualize a string as a chain of characters, such as "Hello", where each character occupies a specific position (index) starting from 0. Strings support operations like concatenation, substring extraction, and searching, making them essential for text processing. Java also provides mutable alternatives like StringBuilder and StringBuffer for scenarios requiring frequent modifications.
Why Use It?
Strings are used to handle and manipulate textual data in a wide range of applications, from user input processing to file parsing. Their immutability in Java ensures thread safety and consistency, making them ideal for constant or shared text data. Strings provide a rich set of methods for searching, modifying, and comparing text, simplifying complex text operations. For performance-critical applications, mutable classes like StringBuilder are used to reduce overhead from creating multiple string objects.
Where to Use? (Real-Life Examples)
- Text Processing: Text editors use strings to store and manipulate document content, such as formatting or searching text.
- Web Development: Web applications use strings to process URLs, HTML content, and user input, such as form data.
- Data Parsing: CSV or JSON parsers use strings to extract fields and values from structured data files.
- Search and Validation: Search engines and form validators use strings to match patterns (e.g., email validation) or search for keywords.
Explain Operations
- Concatenation: This operation combines two strings into a new string. It has a time complexity of O(n), where n is the total length of the resulting string.
- Substring Extraction: This operation extracts a portion of the string based on start and end indices. It has a time complexity of O(n) in Java due to string immutability.
- Search (Index Of): This operation finds the index of a substring or character within the string. It has a time complexity of O(n*m) in the worst case, where n is the string length and m is the substring length.
- Length: This operation returns the number of characters in the string. It has a time complexity of O(1).
- Character Access: This operation retrieves the character at a specific index. It has a time complexity of O(1).
Java Implementation
The following Java code demonstrates common string operations using the String class and introduces StringBuilder for mutable string manipulation.
public class StringExamples {
// Concatenation: Combines two strings
public String concatenate(String str1, String str2) {
return str1 + str2; // Uses + operator for concatenation
}
// Substring: Extracts a portion of the string
public String getSubstring(String str, int start, int end) {
if (start < 0 || end > str.length() || start > end) {
throw new IllegalArgumentException("Invalid start or end index.");
}
return str.substring(start, end); // Returns substring from start to end-1
}
// Search: Finds the index of a substring
public int findIndex(String str, String target) {
return str.indexOf(target); // Returns index of first occurrence or -1 if not found
}
// Length: Returns the number of characters
public int getLength(String str) {
return str.length(); // Returns the string length
}
// Character Access: Gets character at index
public char getCharAt(String str, int index) {
if (index < 0 || index >= str.length()) {
throw new IllegalArgumentException("Invalid index.");
}
return str.charAt(index); // Returns character at specified index
}
// StringBuilder Example: Demonstrates mutable string manipulation
public String buildString(String[] parts) {
StringBuilder builder = new StringBuilder();
for (String part : parts) {
builder.append(part); // Appends each part to the builder
}
return builder.toString(); // Converts StringBuilder to String
}
}
How It Works
- Concatenation:
- The
concatenatemethod uses the+operator, which internally creates a newStringobject combiningstr1andstr2. - For example,
concatenate("Hello", " World")creates a new string "Hello World".
- The
- Substring Extraction:
- The
getSubstringmethod callsstr.substring(start, end), which creates a new string containing characters from indexstarttoend-1. - For example,
getSubstring("Hello", 1, 4)returns "ell".
- The
- Search (Index Of):
- The
findIndexmethod usesstr.indexOf(target)to return the starting index oftargetinstror -1 if not found. - For example,
findIndex("Hello", "ll")returns 2.
- The
- Length:
- The
getLengthmethod callsstr.length()to return the number of characters. - For example,
getLength("Hello")returns 5.
- The
- Character Access:
- The
getCharAtmethod usesstr.charAt(index)to return the character at the specified index. - For example,
getCharAt("Hello", 1)returns 'e'.
- The
- StringBuilder Example:
- The
buildStringmethod usesStringBuilderto efficiently append multiple strings from an array, then converts the result to aString. - For example,
buildString(new String[]{"He", "llo"})returns "Hello".
- The
Complexity Analysis Table
| Operation | Time Complexity | Space Complexity |
|---|---|---|
| Concatenation | O(n) | O(n) |
| Substring Extraction | O(n) | O(n) |
| Search (Index Of) | O(n*m) worst case | O(1) |
| Length | O(1) | O(1) |
| Character Access | O(1) | O(1) |
| StringBuilder Append | O(1) amortized | O(n) |
Note:
- n is the length of the string (or total length for concatenation).
- m is the length of the substring in
indexOf. StringBuilderappend is O(1) amortized due to dynamic resizing.
Key Differences / Notes
- String Immutability:
- Java’s
Stringclass is immutable, so operations like concatenation and substring create new strings, increasing space complexity. StringBuilderandStringBufferare mutable, reducing overhead for frequent modifications.
- Java’s
- String vs. StringBuilder/StringBuffer:
- Use
Stringfor constant or infrequently modified text due to its immutability and thread safety. - Use
StringBuilderfor single-threaded, mutable string operations, orStringBufferfor thread-safe mutable operations.
- Use
- Thread Safety:
StringandStringBuilderare not thread-safe for modifications (thoughStringis immutable).StringBufferis thread-safe but slower.
- String Pool:
- Java maintains a string pool, a special area in the heap (part of the constant pool in the PermGen or Metaspace, depending on the Java version), to store unique string literals and interned strings. When a string literal (e.g.,
"Hello") is created, Java checks the string pool. If the string exists, the reference is reused; otherwise, a new string is added to the pool. This optimizes memory usage for repeated literals. - The
intern()method can manually add a string to the pool, ensuring that identical strings share the same reference. For example,new String("Hello").intern()returns the pooled reference if"Hello"exists. - String pool usage reduces memory overhead but requires careful handling in performance-critical applications, as excessive interning can increase pool size and lookup time.
- Java maintains a string pool, a special area in the heap (part of the constant pool in the PermGen or Metaspace, depending on the Java version), to store unique string literals and interned strings. When a string literal (e.g.,
✅ Tip: Use
StringBuilderfor concatenating strings in a loop to avoid the overhead of creating multipleStringobjects. For simple, one-time concatenations, the+operator is sufficient and readable. UseString.intern()to leverage the string pool for memory optimization when dealing with frequently reused strings.
⚠ Warning: Avoid excessive string concatenation in loops using the
+operator, as it creates multiple intermediateStringobjects, leading to O(n²) time complexity. UseStringBuilderfor better performance. Be cautious withString.intern(), as overusing it can bloat the string pool and degrade performance.
Exercises
- Reverse a String: Write a Java program that reverses a string using both
Stringmethods andStringBuilder. Compare their performance for large strings. - Palindrome Checker: Implement a method to check if a string is a palindrome (ignoring case and non-alphanumeric characters). Test with various inputs.
- String Compression: Create a program that compresses a string by replacing repeated characters with their count (e.g., "aabbb" becomes "a2b3"). Use
StringBuilderfor efficiency. - Substring Frequency: Write a method to count the occurrences of a substring in a string using
indexOf. Test with overlapping and non-overlapping cases. - String Pool Experiment: Write a Java program that demonstrates the string pool by comparing string literals,
new String()objects, and interned strings using==andequals(). Analyze memory usage and equality behavior.