# Arrays, Strings, and StringBuilder in Java

When diving into Java programming, the foundational tools for handling data and text are **Arrays, Strings, and Builders**. Each serves a distinct purpose, and together, they form the backbone of efficient data processing and text manipulation.

**Arrays** offer **fixed-size, contiguous storage**, making them the go-to choice when you need fast, indexed access to elements. Arrays lay the groundwork for more sophisticated data structures such as linked lists, trees, and hash tables. Whether you're storing integers for computation or objects for a sorting algorithm, arrays ensure you have a predictable memory layout and direct access to elements.

**Strings** are **immutable sequences of characters**, playing a critical role in text processing. Because Strings are immutable, they are safe to use across threads and reduce errors from unintended modifications. Immutable objects also facilitate caching, making Strings ideal for handling fixed or frequently reused text data. Think of applications that require processing user input, generating reports, or sending formatted responses — Strings are indispensable.

However, immutability in Strings can become a bottleneck when repeated modifications are necessary. This is where **StringBuilder** (and its thread-safe counterpart, **StringBuffer**) come into play. These mutable classes offer efficient text manipulation by avoiding the creation of multiple intermediate objects. Whether you are **concatenating a series of log entries**, **parsing input data**, or **constructing large text blocks dynamically**, StringBuilder provides a much-needed boost in performance.

#### **Common Scenarios Where They Shine:**

1. **Processing Large Datasets**:  
    Arrays handle large, fixed-size data collections efficiently, making them suitable for batch processing, sorting, and searching operations.
    
2. **Efficient Text Concatenation**:  
    When building a long message from multiple pieces, using a StringBuilder avoids the overhead of creating numerous String objects.
    
3. **Parsing and Formatting Input/Output**:  
    Strings are essential for parsing structured input (like CSV or JSON), while StringBuilder helps format dynamic output for reports or logs.
    

### Arrays in Java

#### **Introduction to Arrays**

**Definition**:  
An array is a fixed-size, contiguous collection of elements of the same data type. Arrays offer direct indexed access, making operations like lookups very efficient. They differ from other collections such as `ArrayList` or `LinkedList` in that their size is determined at the time of creation and cannot be changed dynamically.

**Syntax**:  
Arrays can be declared, initialized, and accessed in the following ways:

```java
// Declaration and initialization
int[] numbers = new int[5];
numbers[0] = 10;

// Inline initialization
int[] values = {1, 2, 3, 4, 5};

// Accessing elements
System.out.println(numbers[0]);  // Output: 10
```

#### **Multi-dimensional Arrays**

Java supports multi-dimensional arrays such as 2D and 3D arrays.

**2D Arrays**:  
A 2D array can be visualized as a table with rows and columns.

```java
int[][] matrix = new int[3][3];

// Initializing elements
matrix[0][0] = 1;
matrix[1][2] = 5;

// Inline initialization
int[][] presetMatrix = {
    {1, 2, 3},
    {4, 5, 6},
    {7, 8, 9}
};
```

**Jagged Arrays**:  
Jagged arrays are multi-dimensional arrays where each row can have a different number of columns.

```java
int[][] jagged = new int[3][];
jagged[0] = new int[2];
jagged[1] = new int[4];
jagged[2] = new int[3];

// Initializing jagged array elements
jagged[0][0] = 1;
jagged[1][2] = 5;
```

#### **Array Operations**

**Insertion**:  
Adding an element at a specific position (note that arrays are fixed in size, so this often involves shifting elements).

```java
int[] numbers = {1, 2, 4, 5};
int pos = 2;
int newValue = 3;

// Shifting elements
for (int i = numbers.length - 1; i > pos; i--) {
    numbers[i] = numbers[i - 1];
}
numbers[pos] = newValue;
```

**Deletion**:  
Removing an element by shifting subsequent elements.

```java
int[] numbers = {1, 2, 3, 4, 5};
int pos = 2;  // Delete the element at index 2

for (int i = pos; i < numbers.length - 1; i++) {
    numbers[i] = numbers[i + 1];
}
```

**Traversal**:  
Looping through array elements.

```java
int[] numbers = {1, 2, 3, 4, 5};

// Using a for loop
for (int i = 0; i < numbers.length; i++) {
    System.out.println(numbers[i]);
}

// Using an enhanced for loop
for (int num : numbers) {
    System.out.println(num);
}
```

#### **Utility Methods (**`Arrays` Class)

The `java.util.Arrays` class provides many useful static methods for array manipulation.

* `sort()`: Sorts the array in ascending order.
    
    ```java
    int[] arr = {3, 1, 4, 1, 5};
    Arrays.sort(arr);
    System.out.println(Arrays.toString(arr));  // Output: [1, 1, 3, 4, 5]
    ```
    
* `binarySearch()`: Searches for a value in a sorted array.
    
    ```java
    int[] arr = {1, 2, 3, 4, 5};
    int index = Arrays.binarySearch(arr, 3);
    System.out.println(index);  // Output: 2
    ```
    
* `copyOf()` / `copyOfRange()`: Creates a copy of the array.
    
    ```java
    int[] arr = {1, 2, 3};
    int[] copy = Arrays.copyOf(arr, 5);
    System.out.println(Arrays.toString(copy));  // Output: [1, 2, 3, 0, 0]
    ```
    
* `equals()`: Compares two arrays for equality.
    
    ```java
    int[] arr1 = {1, 2, 3};
    int[] arr2 = {1, 2, 3};
    System.out.println(Arrays.equals(arr1, arr2));  // Output: true
    ```
    
* `fill()`: Fills the array with a specified value.
    
    ```java
    int[] arr = new int[5];
    Arrays.fill(arr, 7);
    System.out.println(Arrays.toString(arr));  // Output: [7, 7, 7, 7, 7]
    ```
    
* `asList()`: Converts an array to a `List`.
    
    ```java
    String[] words = {"Java", "Python", "C++"};
    List<String> list = Arrays.asList(words);
    System.out.println(list);  // Output: [Java, Python, C++]
    ```
    
* `parallelSort()`: Performs parallel sorting for large datasets.
    
    ```java
    int[] arr = {5, 2, 9, 1, 3};
    Arrays.parallelSort(arr);
    System.out.println(Arrays.toString(arr));  // Output: [1, 2, 3, 5, 9]
    ```
    

#### **Sorting Techniques**

* **Built-in Sorting (**`Arrays.sort()`):
    
    * Uses Dual-Pivot Quicksort for primitive types.
        
    * Uses TimSort for objects (a hybrid of MergeSort and InsertionSort).
        
    
    ```java
    int[] arr = {3, 1, 4, 1, 5};
    Arrays.sort(arr);
    System.out.println(Arrays.toString(arr));  // Output: [1, 1, 3, 4, 5]
    ```
    
* **Parallel Sorting (**`Arrays.parallelSort()`):
    
    * Suitable for large datasets, utilizes multiple threads.
        
    * Internally splits the array into segments and sorts them concurrently.
        
    
    ```java
    int[] arr = {10, 2, 8, 6, 7, 3};
    Arrays.parallelSort(arr);
    System.out.println(Arrays.toString(arr));  // Output: [2, 3, 6, 7, 8, 10]
    ```
    

#### **Best Practices for Arrays**

* **When to Use Arrays**:
    
    * When you need fixed-size collections with fast indexed access.
        
* **Performance Considerations**:
    
    * Prefer arrays for primitive types to avoid the overhead of objects.
        
    * For dynamic sizing, consider using `ArrayList`.
        

### Strings in Java

In Java, a **String** is an immutable sequence of characters. The immutability of Strings means that once a String object is created, its value cannot be changed. Instead, any modification creates a new String object. This property makes Strings safe for use in multithreaded environments and allows them to be cached or pooled, reducing memory overhead.

**Why Immutability Matters:**

* **Thread Safety**: Since Strings are immutable, multiple threads can safely access the same String without synchronization.
    
* **Caching**: The JVM maintains a **string pool** to reduce memory usage. If two String literals have the same value, they refer to the same object in the pool.
    
* **Security**: Immutable Strings are useful in security contexts, such as storing passwords, because they cannot be modified.
    

#### **String Pooling and Memory Management**

When a String is created as a **literal**, it is automatically added to the **string pool** if it doesn't already exist. If a String with the same value exists, the new reference points to the existing String in the pool.

```java
String s1 = "hello";       // Added to the string pool
String s2 = "hello";       // Points to the same object in the pool

System.out.println(s1 == s2);  // Output: true
```

When using the `new` keyword, a new String object is created on the heap, bypassing the string pool.

```java
String s3 = new String("hello");  // New object on the heap
System.out.println(s1 == s3);     // Output: false
```

#### **Creating Strings**

There are two primary ways to create Strings in Java:

1. **String Literals** (Added to the String Pool):
    
    ```java
    String s1 = "hello";
    ```
    
2. **Using** `new String()` (Creates a New Object):
    
    ```java
    String s2 = new String("hello");
    ```
    

#### **Common Methods in the** `String` Class

##### **Manipulation Methods**

* `concat()`: Concatenates the specified string to the end.
    
    ```java
    String str = "Hello";
    String result = str.concat(", World!");
    System.out.println(result);  // Output: Hello, World!
    ```
    
* `toUpperCase()`, `toLowerCase()`: Converts to upper or lower case.
    
    ```java
    String str = "Java";
    System.out.println(str.toUpperCase());  // Output: JAVA
    ```
    
* `trim()`: Removes leading and trailing spaces.
    
    ```java
    String str = "  Hello  ";
    System.out.println(str.trim());  // Output: Hello
    ```
    
* `replace()`, `replaceAll()`: Replaces characters or substrings.
    
    ```java
    String str = "banana";
    System.out.println(str.replace('a', 'o'));  // Output: bonono
    ```
    
* `substring()`: Extracts a substring.
    
    ```java
    String str = "Programming";
    System.out.println(str.substring(0, 4));  // Output: Prog
    ```
    

##### **Search Methods**

* `indexOf()`: Returns the index of the first occurrence of a character or substring.
    
    ```java
    String str = "Hello, World!";
    System.out.println(str.indexOf('W'));  // Output: 7
    ```
    
* `lastIndexOf()`: Returns the last occurrence.
    
    ```java
    System.out.println(str.lastIndexOf('o'));  // Output: 8
    ```
    
* `contains()`: Checks if the string contains a substring.
    
    ```java
    System.out.println(str.contains("World"));  // Output: true
    ```
    
* `startsWith()`, `endsWith()`: Checks the prefix or suffix.
    
    ```java
    System.out.println(str.startsWith("Hello"));  // Output: true
    ```
    

##### **Comparison Methods**

* `equals()`, `equalsIgnoreCase()`: Compares strings for equality.
    
    ```java
    String str1 = "Java";
    String str2 = "java";
    System.out.println(str1.equals(str2));           // Output: false
    System.out.println(str1.equalsIgnoreCase(str2)); // Output: true
    ```
    
* `compareTo()`: Lexicographically compares two strings.
    
    ```java
    System.out.println(str1.compareTo(str2));  // Output: -32
    ```
    
* `matches()`: Checks if the string matches a regex.
    
    ```java
    System.out.println(str1.matches("[Jj]ava"));  // Output: true
    ```
    

##### **Splitting and Joining**

* `split()`: Splits the string based on a delimiter.
    
    ```java
    String data = "apple,banana,cherry";
    String[] fruits = data.split(",");
    for (String fruit : fruits) {
        System.out.println(fruit);
    }
    // Output: apple banana cherry
    ```
    
* `join()`: Joins strings with a delimiter.
    
    ```java
    String result = String.join("-", "2024", "05", "01");
    System.out.println(result);  // Output: 2024-05-01
    ```
    

##### **Character Access**

* `charAt()`: Returns the character at a specified index.
    
    ```java
    String str = "Java";
    System.out.println(str.charAt(1));  // Output: a
    ```
    
* `getChars()`: Copies characters into a char array.
    
    ```java
    char[] chars = new char[3];
    str.getChars(0, 3, chars, 0);
    System.out.println(chars);  // Output: Jav
    ```
    

##### **Length and Empty Check**

* `length()`: Returns the length of the string.
    
    ```java
    String str = "Hello";
    System.out.println(str.length());  // Output: 5
    ```
    
* `isEmpty()`: Checks if the string is empty.
    
    ```java
    System.out.println(str.isEmpty());  // Output: false
    ```
    

### String Interning and Performance

#### **Concept of** `intern()` and String Pools

In Java, **string interning** is a technique where the JVM stores a single copy of each distinct string literal in a common pool, called the **string pool** or **intern pool**. This pool is part of the heap memory and helps optimize memory usage by reusing string literals instead of creating duplicate objects.

When a string is created using a **string literal**, it is automatically placed in the string pool:

```java
String str1 = "hello";
String str2 = "hello";

System.out.println(str1 == str2);  // Output: true (same reference)
```

If a string is created using the `new` keyword, it is stored in the heap rather than the string pool:

```java
String str3 = new String("hello");

System.out.println(str1 == str3);  // Output: false (different references)
```

You can force a string created with `new` to be placed in the string pool using the `intern()` method:

```java
String str4 = new String("hello").intern();

System.out.println(str1 == str4);  // Output: true (both reference the string pool)
```

#### **Memory Optimization with Interning**

String interning reduces memory usage by ensuring that only one copy of each distinct string literal is kept in the pool. This is particularly beneficial when dealing with a large number of duplicate strings.

**Example**:

```java
String name1 = "Java";
String name2 = new String("Java").intern();

System.out.println(name1 == name2);  // Output: true
```

In this example, after calling `intern()`, `name2` references the same string pool object as `name1`.

#### **Immutability and Concatenation Inefficiencies**

Since **Strings are immutable**, any modification, such as concatenation or replacement, results in the creation of a new string object. This can lead to inefficiencies when performing repeated string modifications, especially within loops.

**Example of Inefficient String Concatenation**:

```java
String result = "";
for (int i = 0; i < 5; i++) {
    result += i;  // Each iteration creates a new String object
}
System.out.println(result);  // Output: 01234
```

In the above example, each iteration creates a new string object, which can be very slow and memory-intensive for large loops. To avoid the inefficiencies of immutable strings, Java provides `StringBuilder` for mutable string manipulation. Unlike `String`, `StringBuilder` allows modifications without creating new objects each time.

### **StringBuilder and StringBuffer in Java**

Java provides two classes for mutable sequences of characters: **StringBuilder** and **StringBuffer**. These classes are ideal for situations where repeated string modifications are required.

* **StringBuilder**:  
    A mutable sequence of characters that is **not thread-safe**. This means that multiple threads accessing a `StringBuilder` simultaneously can lead to unpredictable results. It is faster compared to `StringBuffer` due to the absence of synchronization overhead.
    
* **StringBuffer**:  
    Similar to `StringBuilder`, but it is **thread-safe** due to internal **synchronization** (locking mechanisms). This ensures that operations on `StringBuffer` are safe in a multi-threaded environment, but this synchronization makes it **slower** compared to `StringBuilder`.
    

**Thread Safety** in `StringBuffer` means that only one thread can modify the instance at a time, ensuring consistency of data when accessed concurrently by multiple threads.

#### **Creating Instances**

Creating a `StringBuilder` or `StringBuffer` is straightforward:

```java
// Creating a StringBuilder
StringBuilder sb = new StringBuilder("Hello");

// Creating a StringBuffer
StringBuffer sbf = new StringBuffer("Hello");
```

You can also create instances with an initial capacity to avoid frequent resizing:

```java
StringBuilder sb = new StringBuilder(50);
```

#### **Important Methods in** `StringBuilder`

Both `StringBuilder` and `StringBuffer` offer the same set of methods. Here's a list of commonly used methods with examples:

* `append()`: Appends the specified string to the end.
    
    ```java
    StringBuilder sb = new StringBuilder("Hello");
    sb.append(", World!");
    System.out.println(sb);  // Output: Hello, World!
    ```
    
* `insert()`: Inserts a string at a specified position.
    
    ```java
    sb.insert(5, " Java");
    System.out.println(sb);  // Output: Hello Java, World!
    ```
    
* `delete()`: Deletes a sequence of characters between two indices.
    
    ```java
    sb.delete(5, 10);
    System.out.println(sb);  // Output: Hello, World!
    ```
    
* `deleteCharAt()`: Deletes the character at a specified index.
    
    ```java
    sb.deleteCharAt(5);
    System.out.println(sb);  // Output: Hello World!
    ```
    
* `replace()`: Replaces characters between two indices with a specified string.
    
    ```java
    sb.replace(6, 11, "Java");
    System.out.println(sb);  // Output: Hello Java!
    ```
    
* `reverse()`: Reverses the contents of the `StringBuilder`.
    
    ```java
    sb.reverse();
    System.out.println(sb);  // Output: !avaJ olleH
    ```
    
* `capacity()`: Returns the current capacity of the buffer.
    
    ```java
    System.out.println(sb.capacity());
    ```
    
* `ensureCapacity()`: Ensures the buffer has a minimum capacity.
    
    ```java
    sb.ensureCapacity(50);
    ```
    
* `charAt()`: Returns the character at the specified index.
    
    ```java
    System.out.println(sb.charAt(0));  // Output: H
    ```
    
* `setCharAt()`: Sets the character at the specified index.
    
    ```java
    sb.setCharAt(0, 'h');
    System.out.println(sb);  // Output: hello Java!
    ```
    
* `toString()`: Converts the `StringBuilder` to a `String`.
    
    ```java
    String result = sb.toString();
    System.out.println(result);
    ```
    

#### **Code Example**

```java
StringBuilder sb = new StringBuilder("Java");
sb.append(" Programming").insert(0, "Learn ");
System.out.println(sb.toString());  // Output: Learn Java Programming
```

#### **Differences Between** `String`, `StringBuilder`, and `StringBuffer`

| **Feature** | **String** | **StringBuilder** | **StringBuffer** |
| --- | --- | --- | --- |
| **Mutability** | Immutable | Mutable | Mutable |
| **Thread Safety** | Thread-safe | Not thread-safe | Thread-safe |
| **Performance** | Slower for concatenation | Faster | Slower due to synchronization |
| **Use Case** | Fixed or rarely modified strings | Frequent modifications in a single-threaded environment | Frequent modifications in a multi-threaded environment |

### **Regular Expressions with Strings in Java**

#### **Introduction to Regular Expressions**

**Regular Expressions** (often abbreviated as **regex**) are sequences of characters that define a **search pattern**. They are incredibly powerful tools used for tasks such as:

* **Validating input** (e.g., email addresses, phone numbers).
    
* **Searching within text** (finding patterns).
    
* **Replacing parts of text** (e.g., removing special characters).
    
* **Splitting text** based on patterns (e.g., breaking a sentence into words).
    

In Java, the `String` class provides methods like `matches()`, `replaceAll()`, and `split()` that allow you to work with regular expressions effectively.

Java supports regex through the `java.util.regex` package, but for basic use cases, the `String` class methods are sufficient.

### **Regex Basics**

Before diving into methods, let’s understand some basic components of regex:

* **Literal Characters**: Match themselves directly.  
    Example: `a`, `b`, `1`, `A`
    
* **Metacharacters**: Special symbols with special meanings.  
    Example: `.`, `*`, `+`, `?`, `^`, `$`
    
* **Character Classes**: Match any character within a set.  
    Example: `[abc]` matches `a`, `b`, or `c`.
    
* **Quantifiers**: Specify how many times a pattern should occur.  
    Example: `a+` matches one or more `a`s.
    
* **Anchors**: Specify positions in the text.  
    Example: `^` (start of string), `$` (end of string).
    

### **Common Regex Patterns**

Here are some commonly used regex patterns:

* `.` – Matches **any single character** (except newline).
    
* `\d` – Matches a **digit** (0-9).
    
* `\D` – Matches a **non-digit**.
    
* `\w` – Matches a **word character** (letters, digits, and underscores).
    
* `\W` – Matches a **non-word character**.
    
* `\s` – Matches a **whitespace** (space, tab, newline).
    
* `\S` – Matches a **non-whitespace**.
    
* `^` – Anchors the pattern to the **beginning of the string**.
    
* `$` – Anchors the pattern to the **end of the string**.
    
* `*` – Matches **zero or more** occurrences of the preceding character.
    
* `+` – Matches **one or more** occurrences of the preceding character.
    
* `?` – Matches **zero or one** occurrence of the preceding character.
    
* `\` – Escapes a special character.
    

### **Using** `matches()`

The `matches()` method checks whether the entire string matches the given regex pattern. If the pattern matches the whole string, it returns `true`; otherwise, `false`.

#### **Example 1: Validating an Email Address**

```java
String email = "user@example.com";
boolean isValid = email.matches("^[\\w.-]+@[a-zA-Z\\d.-]+\\.[a-zA-Z]{2,}$");

System.out.println(isValid);  // Output: true
```

**Explanation**:

* `^` – Start of the string.
    
* `[\\w.-]+` – One or more word characters (`\w`), dots (`.`), or hyphens (`-`).
    
* `@` – The `@` symbol.
    
* `[a-zA-Z\\d.-]+` – Domain name with letters, digits, dots, or hyphens.
    
* `\\.` – A literal dot (`.`).
    
* `[a-zA-Z]{2,}` – At least two letters for the top-level domain (e.g., `com`, `org`).
    
* `$` – End of the string.
    

#### **Example 2: Checking for a Numeric String**

```java
String number = "12345";
boolean isNumeric = number.matches("\\d+");

System.out.println(isNumeric);  // Output: true
```

**Explanation**:

* `\\d+` – One or more digits (`\d`).
    

### **Using** `replaceAll()`

The `replaceAll()` method replaces all substrings matching the regex pattern with the specified replacement string.

#### **Example 1: Removing All Digits from a String**

```java
String text = "The year is 2024!";
String result = text.replaceAll("\\d", "");

System.out.println(result);  // Output: The year is !
```

**Explanation**:

* `\\d` – Matches any digit.
    
* `""` – Replaces each digit with an empty string (removes it).
    

#### **Example 2: Replacing Multiple Spaces with a Single Space**

```java
String text = "This   is   a   sentence.";
String result = text.replaceAll("\\s+", " ");

System.out.println(result);  // Output: This is a sentence.
```

**Explanation**:

* `\\s+` – One or more whitespace characters.
    
* `" "` – Replaces them with a single space.
    

#### **Example 3: Censoring Words**

```java
String text = "This is a bad word.";
String result = text.replaceAll("bad", "***");

System.out.println(result);  // Output: This is a *** word.
```

### **Using** `split()`

The `split()` method splits the string based on the regex pattern and returns an array of substrings.

#### **Example 1: Splitting a Sentence into Words**

```java
String sentence = "Java is fun to learn!";
String[] words = sentence.split("\\s");

for (String word : words) {
    System.out.println(word);
}
```

**Output**:

```java
Java
is
fun
to
learn!
```

**Explanation**:

* `\\s` – Matches any whitespace character (space, tab, etc.).
    

#### **Example 2: Splitting by Commas**

```java
String data = "apple,banana,grape,orange";
String[] fruits = data.split(",");

for (String fruit : fruits) {
    System.out.println(fruit);
}
```

**Output**:

```java
apple
banana
grape
orange
```

### **Combining Methods for Complex Tasks**

You can combine regex methods to perform more complex operations.

#### **Example: Extracting and Formatting Data**

Suppose you have a string with mixed data and want to extract digits and replace them with formatted numbers.

```java
String data = "Item123Price456";
String formatted = data.replaceAll("(\\D+)(\\d+)", "$1: $2 ");

System.out.println(formatted);  // Output: Item: 123 Price: 456
```

**Explanation**:

* `(\\D+)` – Captures one or more non-digits.
    
* `(\\d+)` – Captures one or more digits.
    
* `"$1: $2 "` – Replaces the matched pattern with the first group (`$1`), followed by `:`, then the second group (`$2`), and a space.
    

## Example Programs

## **1\. Anagram Checker (String)**

### **Problem**

Check if two strings are anagrams. Two strings are anagrams if they contain the same characters in a different order.

### **Solution Approach**

1. Convert both strings to lowercase (to make it case-insensitive).
    
2. Remove any whitespace if necessary.
    
3. Convert both strings to character arrays and sort them.
    
4. Compare the sorted arrays. If they are equal, the strings are anagrams.
    

### **Java Code**

```java
import java.util.Arrays;

public class AnagramChecker {
    public static boolean areAnagrams(String str1, String str2) {
        // Convert to lowercase and remove whitespace
        str1 = str1.toLowerCase().replaceAll("\\s+", "");
        str2 = str2.toLowerCase().replaceAll("\\s+", "");

        // Check if lengths are different
        if (str1.length() != str2.length()) {
            return false;
        }

        // Convert to char arrays and sort
        char[] arr1 = str1.toCharArray();
        char[] arr2 = str2.toCharArray();

        Arrays.sort(arr1);
        Arrays.sort(arr2);

        return Arrays.equals(arr1, arr2);
    }

    public static void main(String[] args) {
        String str1 = "Listen";
        String str2 = "Silent";

        System.out.println(areAnagrams(str1, str2));  // Output: true
    }
}
```

### **Explanation**

1. `toLowerCase()`: Ensures case insensitivity.
    
2. `replaceAll("\\s+", "")`: Removes all whitespace.
    
3. `Arrays.sort()`: Sorts the character arrays.
    
4. `Arrays.equals()`: Compares the sorted arrays.
    

---

## **2\. Reverse Words in a Sentence (StringBuilder)**

### **Problem**

Reverse the order of words in a given sentence.

### **Solution Approach**

1. Split the sentence by spaces into words.
    
2. Use `StringBuilder` to reverse the words efficiently.
    
3. Join the reversed words with spaces.
    

### **Java Code**

```java
public class ReverseWords {
    public static String reverseWords(String sentence) {
        String[] words = sentence.split("\\s+");
        StringBuilder reversed = new StringBuilder();

        for (int i = words.length - 1; i >= 0; i--) {
            reversed.append(words[i]).append(" ");
        }

        return reversed.toString().trim();
    }

    public static void main(String[] args) {
        String sentence = "Java is fun to learn";
        System.out.println(reverseWords(sentence));  // Output: learn to fun is Java
    }
}
```

### **Explanation**

1. `split("\\s+")`: Splits the sentence into words based on one or more spaces.
    
2. `StringBuilder`: Efficient for appending strings.
    
3. `trim()`: Removes any trailing spaces.
    

---

## **3\. Palindrome Checker (String)**

### **Problem**

Check if a given string is a palindrome (reads the same forwards and backwards).

### **Solution Approach**

1. Normalize the string by removing non-alphanumeric characters and converting to lowercase.
    
2. Use two pointers to compare characters from the beginning and end.
    

### **Java Code**

```java
public class PalindromeChecker {
    public static boolean isPalindrome(String str) {
        str = str.replaceAll("[^a-zA-Z0-9]", "").toLowerCase();

        int left = 0;
        int right = str.length() - 1;

        while (left < right) {
            if (str.charAt(left) != str.charAt(right)) {
                return false;
            }
            left++;
            right--;
        }

        return true;
    }

    public static void main(String[] args) {
        String str = "A man, a plan, a canal: Panama";
        System.out.println(isPalindrome(str));  // Output: true
    }
}
```

### **Explanation**

1. `replaceAll("[^a-zA-Z0-9]", "")`: Removes all non-alphanumeric characters.
    
2. **Two pointers**: `left` starts from the beginning, `right` from the end.
    
3. **Compare characters**: If any mismatch occurs, return `false`.
    

---

## **4\. Remove Duplicates from a String (String)**

### **Problem**

Remove all duplicate characters from a string while maintaining the original order.

### **Solution Approach**

1. Use a `LinkedHashSet` to maintain insertion order and ensure uniqueness.
    
2. Convert the set back to a string.
    

### **Java Code**

```java
import java.util.LinkedHashSet;

public class RemoveDuplicates {
    public static String removeDuplicates(String str) {
        LinkedHashSet<Character> set = new LinkedHashSet<>();
        for (char c : str.toCharArray()) {
            set.add(c);
        }

        StringBuilder result = new StringBuilder();
        for (char c : set) {
            result.append(c);
        }

        return result.toString();
    }

    public static void main(String[] args) {
        String str = "programming";
        System.out.println(removeDuplicates(str));  // Output: progamin
    }
}
```

### **Explanation**

1. `LinkedHashSet`: Maintains the order of first occurrences.
    
2. `StringBuilder`: Efficiently constructs the final string without duplicates.
    

---

## **5\. Find the First Non-Repeated Character (String)**

### **Problem**

Find the first character in a string that does not repeat.

### **Solution Approach**

1. Use a `LinkedHashMap` to store character counts while maintaining order.
    
2. Iterate through the string to find the first character with a count of 1.
    

### **Java Code**

```java
import java.util.LinkedHashMap;
import java.util.Map;

public class FirstNonRepeatedChar {
    public static Character firstNonRepeatedChar(String str) {
        LinkedHashMap<Character, Integer> charCount = new LinkedHashMap<>();

        for (char c : str.toCharArray()) {
            charCount.put(c, charCount.getOrDefault(c, 0) + 1);
        }

        for (Map.Entry<Character, Integer> entry : charCount.entrySet()) {
            if (entry.getValue() == 1) {
                return entry.getKey();
            }
        }

        return null;  // No non-repeated character found
    }

    public static void main(String[] args) {
        String str = "swiss";
        System.out.println(firstNonRepeatedChar(str));  // Output: w
    }
}
```

### **Explanation**

1. `LinkedHashMap`: Stores characters and their counts while maintaining order.
    
2. `getOrDefault(c, 0) + 1`: Increments the count for each character.
    

---

## **6\. Count Vowels and Consonants (String)**

### **Problem**

Count the number of vowels and consonants in a given string.

### **Solution Approach**

1. Convert the string to lowercase.
    
2. Iterate through each character and check if it's a vowel or a consonant.
    
3. Count vowels and consonants separately.
    

### **Java Code**

```java
public class VowelConsonantCounter {
    public static void countVowelsAndConsonants(String str) {
        str = str.toLowerCase();
        int vowels = 0, consonants = 0;

        for (char c : str.toCharArray()) {
            if (c >= 'a' && c <= 'z') {
                if ("aeiou".indexOf(c) != -1) {
                    vowels++;
                } else {
                    consonants++;
                }
            }
        }

        System.out.println("Vowels: " + vowels);
        System.out.println("Consonants: " + consonants);
    }

    public static void main(String[] args) {
        String str = "Java Programming";
        countVowelsAndConsonants(str);  
        // Output: Vowels: 5, Consonants: 9
    }
}
```

### **Explanation**

1. **Convert to lowercase** to simplify comparison.
    
2. Check if each character is between `'a'` and `'z'`.
    
3. `"aeiou".indexOf(c) != -1`: Checks if the character is a vowel.
    

---

## **7\. Log Parser with Regular Expressions (String + Regex)**

### **Problem**

Extract information such as timestamps, log levels, and messages from log entries.

### **Solution Approach**

1. Use a regular expression to match timestamps, log levels, and messages.
    
2. Extract and display each part.
    

### **Java Code**

```java
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class LogParser {
    public static void parseLog(String log) {
        String regex = "(\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}) (\\w+): (.*)";
        Pattern pattern = Pattern.compile(regex);
        Matcher matcher = pattern.matcher(log);

        if (matcher.matches()) {
            System.out.println("Timestamp: " + matcher.group(1));
            System.out.println("Log Level: " + matcher.group(2));
            System.out.println("Message: " + matcher.group(3));
        } else {
            System.out.println("Invalid log format");
        }
    }

    public static void main(String[] args) {
        String log = "2024-05-01 12:30:45 ERROR: Something went wrong";
        parseLog(log);
        // Output:
        // Timestamp: 2024-05-01 12:30:45
        // Log Level: ERROR
        // Message: Something went wrong
    }
}
```

### **Explanation**

1. **Regex Breakdown**:
    
    * `(\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2})`: Matches the timestamp.
        
    * `(\\w+)`: Matches the log level (e.g., `ERROR`, `INFO`).
        
    * `(.*)`: Matches the message.
        
2. [`matcher.group`](http://matcher.group)`(n)`: Extracts matched groups.
    

---

## **8\. Longest Substring Without Repeating Characters (String)**

### **Problem**

Find the longest substring without repeating characters.

### **Solution Approach**

1. Use a sliding window and a `HashSet` to track characters.
    
2. Expand the window while characters are unique.
    
3. Update the maximum length when duplicates are encountered.
    

### **Java Code**

```java
import java.util.HashSet;

public class LongestUniqueSubstring {
    public static int longestUniqueSubstring(String str) {
        int maxLength = 0;
        int left = 0;
        HashSet<Character> set = new HashSet<>();

        for (int right = 0; right < str.length(); right++) {
            while (set.contains(str.charAt(right))) {
                set.remove(str.charAt(left));
                left++;
            }
            set.add(str.charAt(right));
            maxLength = Math.max(maxLength, right - left + 1);
        }

        return maxLength;
    }

    public static void main(String[] args) {
        String str = "abcabcbb";
        System.out.println(longestUniqueSubstring(str));  // Output: 3 ("abc")
    }
}
```

### **Explanation**

1. **Sliding Window**: `left` and `right` track the window boundaries.
    
2. `HashSet`: Tracks unique characters in the current window.
    
3. **Update** `maxLength` each time a longer unique substring is found.
    

---

## **9\. String Compression (String)**

### **Problem**

Compress a string by replacing consecutive repeated characters with the character followed by the count.

### **Solution Approach**

1. Iterate through the string.
    
2. Count consecutive characters.
    
3. Append the character and count to a `StringBuilder`.
    

### **Java Code**

```java
public class StringCompression {
    public static String compress(String str) {
        StringBuilder result = new StringBuilder();
        int count = 1;

        for (int i = 0; i < str.length(); i++) {
            if (i < str.length() - 1 && str.charAt(i) == str.charAt(i + 1)) {
                count++;
            } else {
                result.append(str.charAt(i));
                if (count > 1) {
                    result.append(count);
                }
                count = 1;
            }
        }

        return result.toString();
    }

    public static void main(String[] args) {
        String str = "aabbbcc";
        System.out.println(compress(str));  // Output: a2b3c2
    }
}
```

### **Explanation**

1. **Count consecutive characters**: Increment `count` if the next character is the same.
    
2. **Append character and count** to `StringBuilder`.
    
3. **Reset** `count` after each unique character.
    

---

## **10\. Check for Balanced Parentheses (String + Stack)**

### **Problem**

Check if a string contains balanced parentheses `{}`, `[]`, `()`.

### **Solution Approach**

1. Use a `Stack` to track opening brackets.
    
2. For each closing bracket, check if it matches the top of the stack.
    
3. At the end, the stack should be empty if balanced.
    

### **Java Code**

```java
import java.util.Stack;

public class BalancedParentheses {
    public static boolean isBalanced(String str) {
        Stack<Character> stack = new Stack<>();

        for (char c : str.toCharArray()) {
            if (c == '(' || c == '{' || c == '[') {
                stack.push(c);
            } else if (c == ')' && !stack.isEmpty() && stack.peek() == '(') {
                stack.pop();
            } else if (c == '}' && !stack.isEmpty() && stack.peek() == '{') {
                stack.pop();
            } else if (c == ']' && !stack.isEmpty() && stack.peek() == '[') {
                stack.pop();
            } else if (c == ')' || c == '}' || c == ']') {
                return false;
            }
        }

        return stack.isEmpty();
    }

    public static void main(String[] args) {
        String str = "([{}])";
        System.out.println(isBalanced(str));  // Output: true
    }
}
```

### **Explanation**

1. **Push opening brackets** onto the stack.
    
2. **Match closing brackets** with the top of the stack.
    
3. **If mismatched or stack is not empty**, return `false`..
    

---

## **11\. Find the Missing Number in an Array (Array)**

### **Problem**

Given an array of numbers from 1 to `n` with one number missing, find the missing number.

### **Solution Approach**

1. Calculate the sum of numbers from 1 to `n` using the formula ( \\text{sum} = n \\times (n + 1) / 2 ).
    
2. Subtract the sum of array elements from the expected sum to get the missing number.
    

### **Java Code**

```java
public class MissingNumber {
    public static int findMissingNumber(int[] arr, int n) {
        int expectedSum = n * (n + 1) / 2;
        int actualSum = 0;

        for (int num : arr) {
            actualSum += num;
        }

        return expectedSum - actualSum;
    }

    public static void main(String[] args) {
        int[] arr = {1, 2, 4, 5};
        int n = 5;
        System.out.println(findMissingNumber(arr, n));  // Output: 3
    }
}
```

### **Explanation**

1. **Expected Sum**: Calculate the sum of numbers from 1 to `n`.
    
2. **Actual Sum**: Sum the elements in the array.
    
3. **Difference**: The missing number is the difference between the expected and actual sums.
    

---

## **12\. Find the Largest and Smallest Elements (Array)**

### **Problem**

Find the largest and smallest elements in an array.

### **Solution Approach**

1. Traverse the array and track the largest and smallest elements.
    

### **Java Code**

```java
public class LargestAndSmallest {
    public static void findLargestAndSmallest(int[] arr) {
        int largest = arr[0];
        int smallest = arr[0];

        for (int num : arr) {
            if (num > largest) {
                largest = num;
            }
            if (num < smallest) {
                smallest = num;
            }
        }

        System.out.println("Largest: " + largest);
        System.out.println("Smallest: " + smallest);
    }

    public static void main(String[] args) {
        int[] arr = {3, 1, 4, 1, 5, 9, 2};
        findLargestAndSmallest(arr);
        // Output: Largest: 9, Smallest: 1
    }
}
```

### **Explanation**

1. **Initialize** `largest` and `smallest` with the first element.
    
2. Traverse the array and update `largest` and `smallest` accordingly.
    

---

## **13\. Rotate an Array (Array)**

### **Problem**

Rotate an array to the right by a given number of steps.

### **Solution Approach**

1. Use the modulo operator to handle rotations greater than the array length.
    
2. Reverse parts of the array to achieve rotation.
    

### **Java Code**

```java
import java.util.Arrays;

public class RotateArray {
    public static void rotate(int[] arr, int steps) {
        int n = arr.length;
        steps %= n;  // Handle steps greater than array length

        reverse(arr, 0, n - 1);
        reverse(arr, 0, steps - 1);
        reverse(arr, steps, n - 1);
    }

    private static void reverse(int[] arr, int start, int end) {
        while (start < end) {
            int temp = arr[start];
            arr[start] = arr[end];
            arr[end] = temp;
            start++;
            end--;
        }
    }

    public static void main(String[] args) {
        int[] arr = {1, 2, 3, 4, 5};
        rotate(arr, 2);
        System.out.println(Arrays.toString(arr));  // Output: [4, 5, 1, 2, 3]
    }
}
```

### **Explanation**

1. **Reverse the entire array**.
    
2. **Reverse the first** `steps` elements.
    
3. **Reverse the remaining elements**.
    

---

## **14\. Merge Two Sorted Arrays (Array)**

### **Problem**

Merge two sorted arrays into one sorted array.

### **Solution Approach**

1. Use two pointers to traverse both arrays.
    
2. Compare elements and add the smaller one to the result array.
    

### **Java Code**

```java
import java.util.Arrays;

public class MergeSortedArrays {
    public static int[] merge(int[] arr1, int[] arr2) {
        int[] result = new int[arr1.length + arr2.length];
        int i = 0, j = 0, k = 0;

        while (i < arr1.length && j < arr2.length) {
            if (arr1[i] <= arr2[j]) {
                result[k++] = arr1[i++];
            } else {
                result[k++] = arr2[j++];
            }
        }

        while (i < arr1.length) {
            result[k++] = arr1[i++];
        }

        while (j < arr2.length) {
            result[k++] = arr2[j++];
        }

        return result;
    }

    public static void main(String[] args) {
        int[] arr1 = {1, 3, 5};
        int[] arr2 = {2, 4, 6};
        int[] merged = merge(arr1, arr2);
        System.out.println(Arrays.toString(merged));  // Output: [1, 2, 3, 4, 5, 6]
    }
}
```

### **Explanation**

1. **Two Pointers**: `i` for `arr1`, `j` for `arr2`, and `k` for `result`.
    
2. **Merge** elements in sorted order.
    
3. **Copy remaining elements** from either array.
