Java Coding Interview Problems
This page presents several common Java coding interview questions, complete with solutions, complexity analysis, and considerations for edge cases.
Nth Fibonacci Number (O(1) Space)
The Fibonacci sequence is where each number is the sum of the two preceding ones (0, 1, 1, 2, 3, 5...). This solution uses a loop for efficient calculation with constant space.
FibonacciSeries.java
import java.util.Scanner;
public class FibonacciSeries {
public static void main(String argvs[]) {
Scanner scnr = new Scanner(System.in);
int N = scnr.nextInt();
int first = 0;
int second = 1;
if (N <= 0) {
System.out.println("N can never be zero or negative");
return;
}
if (N == 1) {
System.out.println(first);
} else if (N == 2) {
System.out.println(second);
} else {
int curr = 0;
for (int j = 3; j <= N; j++) {
curr = first + second;
first = second;
second = curr;
}
System.out.println("The " + N + "th Fibonacci number is: " + curr);
}
}
}
Example Output
Input: 6
Output: The 6th Fibonacci number is: 5
Complexity: O(n) time, O(1) space. Corner Cases: Handles negative or zero input.
Counting Digits in a Number
This program counts the digits in an integer by repeatedly dividing by 10.
CountDigits.java
import java.util.Scanner;
public class CountDigits {
public static int cntDig(int n) {
if (n == 0) return 1;
if (n < 0) n = -n;
int cnt = 0;
while (n != 0) {
n = n / 10;
cnt = cnt + 1;
}
return cnt;
}
public static void main(String argvs[]) {
Scanner scnr = new Scanner(System.in);
int N = scnr.nextInt();
System.out.println("The total digits in number " + N + " are: " + cntDig(N));
}
}
Example Output
Input: 0
Output: The total digits in number 0 are: 1
Input: -980
Output: The total digits in number -980 are: 3
Complexity: O(log10(n)) time, O(1) space. Corner Cases: Handles zero and negative numbers.
Counting Specific Digits
This program counts occurrences of a specific digit within a number.
CountDigitsD.java
import java.util.Scanner;
public class CountDigitsD {
public static int cntDig(int n, int d) {
if (n == 0 && d == 0) return 1;
if (n < 0) n = -n;
int cnt = 0;
while (n != 0) {
int dig = n % 10;
if (dig == d) cnt++;
n = n / 10;
}
return cnt;
}
public static void main(String argvs[]) {
Scanner scnr = new Scanner(System.in);
int N = scnr.nextInt();
int D = scnr.nextInt();
System.out.println("The total count of digit " + D + " occurring in the number " + N + " is: " + cntDig(N, D));
}
}
Example Output
Input: 121 1
Output: The total count of digit 1 occurring in the number 121 is: 2
Complexity: O(log10(n)) time, O(1) space. Corner Cases: Handles zero and negative numbers.
Calculating an Recursively
This program calculates a number raised to a power using recursion.
PowerN.java
import java.util.Scanner;
public class PowerN {
public static double findPower(double y, int n) {
if (n == 0) return 1.0;
double pwr = findPower(y, n - 1);
return y * pwr;
}
public static double powr(double Y, int N) {
if (N < 0) return 1.0 / findPower(Y, -N);
return findPower(Y, N);
}
public static void main(String argvs[]) {
Scanner scnr = new Scanner(System.in);
double y = scnr.nextDouble();
int N = scnr.nextInt();
System.out.println("The value of " + y + " ^ " + N + " is: " + powr(y, N));
}
}
Example Output
Input: 2.2 2
Output: The value of 2.2 ^ 2 is: 4.840000000000001
Complexity: O(n) time, O(1) space. Corner Cases: Handles negative exponents.
Toggling Case of String Characters
This program toggles the case of each character in a string (uppercase to lowercase, lowercase to uppercase).
ToggleString.java
import java.util.Scanner;
public class ToggleString {
public static void main(String argvs[]) {
Scanner scnnr = new Scanner(System.in);
String strng = scnnr.nextLine();
StringBuilder result = new StringBuilder("");
for (int i = 0; i < strng.length(); i++) {
char chr = strng.charAt(i);
if (chr >= 'A' && chr <= 'Z') result.append((char) (chr + 32));
else if (chr >= 'a' && chr <= 'z') result.append((char) (chr - 32));
else result.append(chr);
}
String answer = result.toString();
System.out.println("After toggling, the string " + strng + " becomes: " + answer);
}
}
Example Output
Input: jaVaTpoiNt
Output: After toggling, the string jaVaTpoiNt becomes: JAvAtPOInT
Complexity: O(n) time, O(n) space. Corner Cases: Handles non-alphabetic characters.
Finding Unique Characters in a String
This program identifies and prints the unique characters from a string containing only lowercase letters.
UniqueChar.java
import java.util.Scanner;
public class UniqueChar {
public static String findUniqueChar(String inputStr) {
int arr[] = new int[256];
for (int j = 0; j < inputStr.length(); j++) {
char ch = inputStr.charAt(j);
arr[ch - 'a']++;
}
String str = "";
for (int i = 0; i < 256; i++) {
if (arr[i] == 1) str += (char) (i + 'a');
}
return str;
}
public static void main(String argvs[]) {
Scanner scn = new Scanner(System.in);
String str = scn.nextLine();
String s = findUniqueChar(str);
if (s.length() == 0) {
System.out.println("There is no unique characters present in the string: '" + str + "'");
} else {
System.out.println("The unique characters present in the string: '" + str + "' are: ");
for (int i = 0; i < s.length(); i++) {
System.out.print(s.charAt(i) + " ");
}
}
}
}
Example Output
Input: pppdaf
Output: The unique characters present in the string: 'pppdaf' are: a d f
Complexity: O(n) time, O(1) space. Corner Cases: Handles empty and strings with no unique characters.
Proving String Immutability
This program demonstrates string immutability in Java by showing that modifying a string creates a new string object, leaving the original unchanged.
ImmutableStrings.java
public class ImmutableStrings {
public static void main(String argvs[]) {
String str1 = "dadi";
String str2 = str1;
str1 = str1 + "Gulzar";
if (str1 == str2) {
System.out.println("Strings are not immutable.");
} else {
System.out.println("Strings are immutable."); // This will be printed
}
}
}
Reversing an Array Using Recursion
This program recursively reverses and prints an array without using extra space (besides the call stack).
ReverseArrUsingRec.java
import java.util.*;
public class ReverseArrUsingRec {
public static void revArr(int arr[], int size, int i) {
if (i >= size) return;
revArr(arr, size, i + 1);
System.out.print(arr[i] + " ");
}
public static void main(String argvs[]) {
Scanner scn = new Scanner(System.in);
int n = scn.nextInt();
int intArr[] = new int[n];
for (int i = 0; i < n; i++) intArr[i] = scn.nextInt();
System.out.println("The input array is:");
for (int it = 0; it < intArr.length; it++) System.out.print(intArr[it] + " ");
System.out.println(" \n");
System.out.println("The reversed array is:");
revArr(intArr, intArr.length, 0);
}
}
Complexity: O(n) time, O(1) space (excluding recursion stack).
Finding First and Last Index of an Element
This program efficiently finds the first and last index of a target element in an array using a single pass.
FindEle.java
import java.util.*;
public class FindEle {
public static int[] findIndexOfReqEle(int inputArr[], int tar) {
int size = inputArr.length;
int ansArr[] = new int[2];
ansArr[0] = -1;
ansArr[1] = -1;
boolean isEleFound = false;
for (int k = 0; k < size; k++) {
if (inputArr[k] == tar) {
if (!isEleFound) {
ansArr[0] = k + 1;
ansArr[1] = k + 1;
isEleFound = true;
} else {
ansArr[1] = k + 1;
}
}
}
return ansArr;
}
public static void main(String argvs[]) {
System.out.println("Enter size of the input array");
Scanner scnr = new Scanner(System.in);
int num = scnr.nextInt();
int[] intArr = new int[num];
System.out.println("Enter elements of the input array");
for (int k = 0; k < num; k++) intArr[k] = scnr.nextInt();
System.out.println("Enter target element to be searched.");
int targetEle = scnr.nextInt();
int ansArr[] = findIndexOfReqEle(intArr, targetEle);
if (ansArr[0] == -1) {
System.out.println("In the input array, the element " + targetEle + " does not exist.");
} else {
System.out.println("For the target element: " + targetEle + ", First Index = " + ansArr[0] + " Last Index = " + ansArr[1]);
}
}
}
Complexity: O(n) time, O(1) space. Corner Cases: Handles cases where the target element is not present.
Wave Order Matrix Traversal
This program traverses a matrix in a "wave" pattern: left-to-right, right-to-left, alternating across columns.
WaveOrder.java
import java.io.*;
import java.util.*;
public class WaveOrder {
public static void main(String argvs[]) throws Exception {
Scanner scnr = new Scanner(System.in);
System.out.println("Enter row: ");
int r = scnr.nextInt();
System.out.println("Enter column: ");
int c = scnr.nextInt();
int[][] matrx = new int[r][c];
System.out.println("Enter the elements of the matrix: ");
for (int i = 0; i < r; i++) {
for (int j = 0; j < c; j++) {
matrx[i][j] = scnr.nextInt();
}
}
System.out.println("\n");
System.out.println("The wave order traversal of the input matrix is: ");
for (int j = 0; j < matrx[0].length; j++) {
if (j % 2 == 0) {
for (int i = 0; i < matrx.length; i++) System.out.print(matrx[i][j] + " ");
} else {
for (int i = matrx.length - 1; i >= 0; i--) System.out.print(matrx[i][j] + " ");
}
System.out.println();
}
}
}
Complexity: O(r*c) time, O(1) space.
Implementing a "Developer" Class
This example shows how to create a simple Developer
class with properties (age, name), methods (setters, getters), and a method to demonstrate behavior (writeCode()
).
TestClass.java
import java.util.*;
class Developer {
private int devAge;
private String devName;
Developer() {}
Developer(int devAge, String devName) {
this.devAge = devAge;
this.devName = devName;
}
void setAge(int devAge) { this.devAge = devAge; }
void setName(String devName) { this.devName = devName; }
int getAge() { return devAge; }
String getName() { return devName; }
public void writeCode() {
System.out.println(this.devName + " writes codes.");
}
}
Counting Vowels and Consonants
This program counts the vowels, consonants, and other characters (non-alphabetic) in a given string containing only lowercase letters and numbers.
CountVowelConst.java
import java.util.*;
public class CountVowelConst {
public static boolean isVowelChar(char chr) {
char charArr[] = new char[5];
charArr[0] = 'a'; charArr[1] = 'e'; charArr[2] = 'i'; charArr[3] = 'o'; charArr[4] = 'u';
for (int g = 0; g < 5; g++) {
if (chr == charArr[g]) return true;
}
return false;
}
public static void main(String argvs[]) {
Scanner scnr = new Scanner(System.in);
String strng = scnr.nextLine();
int vCount = 0;
int cCount = 0;
for (int i = 0; i < strng.length(); i++) {
char chr = strng.charAt(i);
if (isVowelChar(chr)) vCount++;
else if (chr >= 'a' && chr <= 'z' && !isVowelChar(chr)) cCount++;
}
System.out.println("Count of vowels in the String : '" + strng + "' is: " + vCount);
System.out.println("Count of consonants in the String : '" + strng + "' is: " + cCount);
System.out.println("Count of other characters in the String : '" + strng + "' is: " + (strng.length() - vCount - cCount));
}
}
Example Output
Input: abcd44iut
Output:
Count of vowels in the String : 'abcd44iut' is: 3
Count of consonants in the String : 'abcd44iut' is: 4
Count of other characters in the String : 'abcd44iut' is: 2
Complexity: O(n) time, O(1) space. Corner Cases: Handles non-alphabetic characters correctly.
Demonstrating Inheritance
This example shows a simple inheritance scenario where a Smartphone
class extends the Mobile
class, inheriting its properties and methods and adding its own.
TestInheritance.java
class Mobile {
private int num;
Mobile() {}
void setNumber(int num) { this.num = num; }
int getNumber() { return num; }
public void doCall() { System.out.println("Calling the dialled number."); }
public void receiveMessage() { System.out.println("The Message is received."); }
}
class SmrtPhone extends Mobile {
int cameraMegaPX;
public void clickPhoto() { System.out.println("A photo is clicked."); }
public void playMsic() { System.out.println("Music is getting Played."); }
public void pauseMusic() { System.out.println("Music player Paused."); }
public void stpMusic() { System.out.println("Music player Stopped."); }
}
public class TestInheritance {
public static void main(String argvs[]) {
SmrtPhone sp = new SmrtPhone();
sp.setNumber(94863472);
System.out.println("Phone number is: " + sp.getNumber());
sp.doCall(); sp.playMsic(); sp.pauseMusic(); sp.stpMusic(); sp.clickPhoto();
}
}
Handling Divide by Zero Exception
This program demonstrates how to use a try-catch
block to handle a ArithmeticException
(divide by zero) gracefully.
DivideByZeroException.java
import java.util.*;
public class DivideByZeroException {
public static void main(String argvs[]) {
Scanner scnr = new Scanner(System.in);
int num = scnr.nextInt();
System.out.println("The number " + num + " is divided by zero.");
try {
System.out.println(num / 0);
} catch (Exception ex) {
System.out.println(ex);
}
System.out.println("Program is completed.");
}
}
Demonstrating a Single Thread
This program shows how to get and modify the properties of the main thread in a Java program.
MainThreadDemo.java
public class MainThreadDemo {
public static void main(String[] argvs) {
Thread th = Thread.currentThread();
th.setName("The Main Thread");
th.setPriority(7);
System.out.println(th);
System.out.println(th.getName());
System.out.println(th.getPriority());
}
}
Palindrome Check (Case-Insensitive)
This program checks if a string is a palindrome, ignoring case, whitespace, and non-alphanumeric characters.
CheckPalindrome.java
import java.util.*;
public class CheckPalindrome {
public static boolean isStrngPalindrome(String stng) {
int l = 0;
int h = stng.length() - 1;
while (l < h) {
char c1 = stng.charAt(l);
char c2 = stng.charAt(h);
if (c1 != c2) return false;
l++; h--;
}
return true;
}
public static boolean isSntncePalindrome(String sntnce) {
String reslt = "";
for (int g = 0; g < sntnce.length(); g++) {
char chr = sntnce.charAt(g);
if ((chr >= 'a' && chr <= 'z') || (chr >= 'A' && chr <= 'Z') || (chr >= '0' && chr <= '9')) {
if (chr >= 'A' && chr <= 'Z') reslt += (char) (chr + 32);
else reslt += chr;
}
}
return isStrngPalindrome(reslt);
}
public static void main(String argvs[]) {
Scanner scnr = new Scanner(System.in);
String sentnce = scnr.nextLine();
if (isSntncePalindrome(sentnce)) {
System.out.println("The string '" + sentnce + "' is a palindrome.");
} else {
System.out.println("The string '" + sentnce + "' is not a palindrome.");
}
}
}
Complexity: O(n) time, O(1) space. Corner Cases: Handles strings with whitespace and non-alphanumeric characters.
Binary String Addition
This program adds two binary strings, handling carries and potential differences in string lengths.
AddBinaryStrings.java
import java.util.*;
public class AddBinaryStrings {
public static String addStrings(String s1, String s2) {
String resultantStr = "";
if (s1.equals("0") && s2.equals("0")) return "0";
int p = s1.length() - 1;
int q = s2.length() - 1;
int cary = 0;
while (p >= 0 || q >= 0 || cary > 0) {
int dig1 = (p >= 0) ? (s1.charAt(p) - '0') : 0;
int dig2 = (q >= 0) ? (s2.charAt(q) - '0') : 0;
int digit = 0;
if (dig1 + dig2 + cary >= 2) {
digit = (dig1 + dig2 + cary) % 2;
cary = (dig1 + dig2 + cary) / 2;
} else {
digit = dig1 + dig2 + cary;
cary = 0;
}
p--; q--;
resultantStr = digit + resultantStr;
}
return resultantStr;
}
public static void main(String argvs[]) {
Scanner scnr = new Scanner(System.in);
String s1 = scnr.nextLine();
String s2 = scnr.nextLine();
System.out.println("The addition of binary strings is: " + addStrings(s1, s2));
}
}
Example Output
Input: 11001 100
Output: The addition of binary strings is: 11101
Complexity: O(max(m, n)) time, O(1) space. Corner Cases: Handles strings of different lengths and potential extra bits in the result.
Checking for Anagrams
This program efficiently checks if two strings are anagrams (containing the same characters with the same frequency) using a HashMap.
StringAnagrams.java
import java.util.*;
public class StringAnagrams {
public static boolean isStringsAnagram(String a1, String a2) {
if (a1.length() != a2.length()) return false;
HashMap<Character, Integer> hashMap = new HashMap<>();
for (int g = 0; g < a1.length(); g++) {
int ordrFreq = hashMap.getOrDefault(a1.charAt(g), 0);
hashMap.put(a1.charAt(g), ordrFreq + 1);
}
for (int g = 0; g < a2.length(); g++) {
if (!hashMap.containsKey(a2.charAt(g)) || hashMap.get(a2.charAt(g)) == 0) return false;
else {
int ordrFreq = hashMap.get(a2.charAt(g));
hashMap.put(a2.charAt(g), ordrFreq - 1);
}
}
return true;
}
public static void main(String argvs[]) {
Scanner scnr = new Scanner(System.in);
String a1 = scnr.nextLine();
String a2 = scnr.nextLine();
if (isStringsAnagram(a1, a2)) {
System.out.println("Strings '" + a1 + "' & '" + a2 + "' are anagrams.");
} else {
System.out.println("Strings '" + a1 + "' & '" + a2 + "' are not anagrams.");
}
}
}
Complexity: O(m + n) time, O(1) space. Corner Cases: Handles strings of different lengths.
Finding Index in a Sorted Array
This program uses binary search to find the index of a target element in a sorted array. If the element is not found, it returns the index where it should be inserted to maintain the sorted order.
SortedArrayIndex.java
public class SortedArrayIndex {
public static int findIndex(int[] numsArr, int tar) {
int l = 0;
int h = numsArr.length - 1;
while (l <= h) {
int middle = l + (h - l) / 2;
if (numsArr[middle] == tar) return middle;
else if (numsArr[middle] < tar) l = middle + 1;
else h = middle - 1;
}
return l;
}
public static void main(String argvs[]) {
int[] inputArr = {2, 6, 9, 13, 24, 35, 78, 90, 99};
int size = inputArr.length;
System.out.println("For the input array: ");
for (int j = 0; j < size; j++) System.out.print(inputArr[j] + " ");
int tar = 7;
int ans = findIndex(inputArr, tar);
System.out.println();
System.out.println("The index of the target element " + tar + " is: " + ans);
}
}
Complexity: O(log n) time, O(1) space.
Wave Sorting an Array
This program sorts an array in a wave-like pattern (peak-valley-peak-valley) in linear time and constant space.
WaveSort.java
public class WaveSort {
public static void swapEle(int[] inputArr, int a1, int a2) {
int t = inputArr[a1];
inputArr[a1] = inputArr[a2];
inputArr[a2] = t;
}
public static void waveSorting(int[] inputArr) {
for (int i1 = 0; i1 < inputArr.length; i1 = i1 + 2) {
if (i1 > 0 && inputArr[i1 - 1] > inputArr[i1]) swapEle(inputArr, i1 - 1, i1);
if (i1 < inputArr.length - 1 && inputArr[i1 + 1] > inputArr[i1]) swapEle(inputArr, i1, i1 + 1);
}
}
public static void main(String argvs[]) {
int[] inputArr = {19, 18, 16, 13, 14, 17, 12};
int size = inputArr.length;
System.out.println("The input array is: ");
for (int i1 = 0; i1 < size; i1++) System.out.print(inputArr[i1] + " ");
System.out.println("\n ");
waveSorting(inputArr);
System.out.println("After the wave sort");
for (int i1 = 0; i1 < inputArr.length; i1++) System.out.print(inputArr[i1] + " ");
}
}
Complexity: O(n) time, O(1) space.
Creating and Handling a Custom Exception
This example demonstrates creating a custom exception class (LowBalanceException
) and handling it using a try-catch
block. The exception is thrown when a bank account's balance falls below a minimum threshold.
BankBlance.java
public class BankBlance {
public static void main(String[] argvs) {
BankAccount acnt1 = new BankAccount(500);
BankAccount acnt2 = new BankAccount();
acnt2.setBalance(500);
BankAccount acnt3 = new BankAccount(10000);
System.out.println("account - 1 balance = " + acnt1.getBlnce());
System.out.println("account - 2 balance = " + acnt2.getBlnce());
System.out.println("account - 3 balance = " + acnt3.getBlnce());
}
}
class BankAccount {
private int blnce;
BankAccount() { blnce = 7000; }
BankAccount(int blnce) {
try {
if (blnce >= 7000) {
this.blnce = blnce;
System.out.println("The bank account is created and the balance is set to: " + blnce);
} else {
this.blnce = 0;
System.out.println("The account can't be created.");
throw new LowBalanceExcption();
}
} catch (LowBalanceExcption e) {
System.out.println(e);
}
}
void setBalance(int balance) {
try {
if (blnce >= 7000) {
this.blnce = blnce;
System.out.println("The account has been created and the bank balance has set to: " + blnce);
} else {
this.blnce = 0;
System.out.println(" The account can't be created.");
throw new LowBalanceExcption();
}
} catch (LowBalanceExcption e) {
System.out.println(e);
}
}
int getBlnce() { return blnce; }
}
class LowBalanceExcption extends Exception {
public String toString() {
return "Account has the low balance: The bank balance can never be less than Rs.7000/-";
}
}
Multiple Inheritance Using Interfaces
Java doesn't support multiple inheritance of classes, but you can achieve a similar effect using interfaces. This example shows a class implementing multiple interfaces, each defining a printMessage()
method. The `super` keyword is used to call the specific interface method.
TestClass.java
interface Interface1 {
default void printMessage() { System.out.println("Default Interface1"); }
}
interface Interface2 {
default void printMessage() { System.out.println("Default Interface2"); }
}
public class TestClass implements Interface1, Interface2 {
@Override
public void printMessage() {
Interface1.super.printMessage();
Interface2.super.printMessage();
}
public void showOfInterface1() { Interface1.super.printMessage(); }
public void showOfInterface2() { Interface2.super.printMessage(); }
public static void main(String argvs[]) {
TestClass tc = new TestClass();
tc.printMessage();
System.out.println("Now Executing methods showOfInterface1() showOfInterface2()");
tc.showOfInterface1();
tc.showOfInterface2();
}
}
Nested Classes
This example shows how to define inner classes (nested classes) within an outer class in Java. Note the use of `static` for the inner classes to make them independent of the outer class instance.
NestedClass.java
public class NestedClass {
public static void main(String argvs[]) {
OuterClass ob1 = new OuterClass(10, 20);
OuterClass.InnerClass2 ob2 = new OuterClass.InnerClass2(40);
ob2.displayData();
OuterClass.InnerClass3.z1 = 101;
System.out.println(OuterClass.InnerClass3.z1);
OuterClass.InnerClass3 ob3 = new OuterClass.InnerClass3(409);
ob3.displayData();
}
}
class OuterClass {
private int x1;
private int y1;
OuterClass() { System.out.println("The default constructor of the Outer class is invoked."); }
OuterClass(int x1, int y1) {
this.x1 = x1;
this.y1 = y1;
System.out.println("The parameterized constructor of the Outer class is invoked.");
}
void displayData() { System.out.println("X = " + x1 + " and Y = " + y1); }
class InnerClass1 {
int z1 = 0;
InnerClass1() { System.out.println("The default constructor invoked of the first inner class."); }
InnerClass1(int z1) { this.z1 = z1; }
void displayData() { System.out.println("X = " + x1 + " Y = " + y1 + " and Z = " + z1); }
}
static class InnerClass2 {
int z1 = 0;
InnerClass2() { System.out.println("The default constructor invoked of the second inner class."); }
InnerClass2(int z1) { this.z1 = z1; System.out.println("The parameterized constructor invoked of the second inner class."); }
void displayData() { System.out.println("Z = " + z1); }
}
static class InnerClass3 {
static int z1 = 0;
InnerClass3() { System.out.println("The default constructor invoked of the third inner class."); }
InnerClass3(int a1) { z1 = a1; System.out.println("The parameterized constructor invoked of the third inner class."); }
void displayData() { System.out.println("Z = " + z1); }
}
}
The Diamond Problem (Multiple Inheritance)
Java doesn't directly support multiple inheritance of classes to avoid the "diamond problem". The diamond problem occurs when a class inherits from two classes that have a common ancestor, and both parents have a method with the same signature. The compiler is unclear about which method to inherit.
[Illustrative code demonstrating the compilation error that occurs in Java when attempting to create a class that extends multiple classes with the same method signatures would be included here.]
Thread Methods: isAlive()
and join()
The isAlive()
method checks if a thread is currently running. The join()
method makes the calling thread wait until the specified thread completes its execution.
DemoClass.java
class DemoThread1 extends Thread {
public void run() {
System.out.println("DemoThread1 is running");
}
}
public class DemoClass {
public static void main(String[] argvs) throws InterruptedException {
DemoThread1 d1 = new DemoThread1();
d1.start();
System.out.println("DemoThread1 is Alive? " + d1.isAlive());
d1.join();
System.out.println("DemoThread1 is Alive? " + d1.isAlive());
}
}
Binary String Addition (Detailed)
This program adds two binary strings, handling carries and considering the possibility of strings with different lengths. The algorithm iterates through the strings from right to left, performing bitwise addition with carry handling. The result may have one more digit than the input strings.
AddBinaryStrings.java
import java.util.*;
public class AddBinaryStrings {
public static String addStrings(String s1, String s2) {
String resultantStr = "";
if (s1.equals("0") && s2.equals("0")) return "0";
int p = s1.length() - 1;
int q = s2.length() - 1;
int cary = 0;
while (p >= 0 || q >= 0 || cary > 0) {
int dig1 = (p >= 0) ? (s1.charAt(p) - '0') : 0;
int dig2 = (q >= 0) ? (s2.charAt(q) - '0') : 0;
int digit = 0;
if (dig1 + dig2 + cary >= 2) {
digit = (dig1 + dig2 + cary) % 2;
cary = (dig1 + dig2 + cary) / 2;
} else {
digit = dig1 + dig2 + cary;
cary = 0;
}
p--;
q--;
resultantStr = digit + resultantStr;
}
return resultantStr;
}
public static void main(String argvs[]) {
Scanner scnr = new Scanner(System.in);
String s1 = scnr.nextLine();
String s2 = scnr.nextLine();
System.out.println("The addition of binary strings is: " + addStrings(s1, s2));
}
}
Example Output
Input: 111 1
Output: The addition of binary strings is: 1000
Complexity: O(max(m, n)) time, O(1) space.
Checking for Anagrams (Efficiently)
This program determines whether two strings are anagrams of each other (containing the same characters, irrespective of order and ignoring case). It uses a HashMap to efficiently track character frequencies.
StringAnagrams.java
import java.util.*;
public class StringAnagrams {
public static boolean isStringsAnagram(String a1, String a2) {
if (a1.length() != a2.length()) return false;
HashMap<Character, Integer> hashMap = new HashMap<>();
for (int g = 0; g < a1.length(); g++) {
char ch = Character.toLowerCase(a1.charAt(g)); // ignore case
int ordrFreq = hashMap.getOrDefault(ch, 0);
hashMap.put(ch, ordrFreq + 1);
}
for (int g = 0; g < a2.length(); g++) {
char ch = Character.toLowerCase(a2.charAt(g)); // ignore case
if (!hashMap.containsKey(ch) || hashMap.get(ch) == 0) return false;
else {
int ordrFreq = hashMap.get(ch);
hashMap.put(ch, ordrFreq - 1);
}
}
return true;
}
public static void main(String argvs[]) {
Scanner scnr = new Scanner(System.in);
String a1 = scnr.nextLine();
String a2 = scnr.nextLine();
if (isStringsAnagram(a1, a2)) {
System.out.println("Strings '" + a1 + "' & '" + a2 + "' are anagrams.");
} else {
System.out.println("Strings '" + a1 + "' & '" + a2 + "' are not anagrams.");
}
}
}
Complexity: O(m + n) time, O(1) space (assuming a limited character set).
Thread States and Synchronization
This section demonstrates thread states (alive, terminated) using `isAlive()` and thread synchronization using the `synchronized` keyword to prevent race conditions.
DemoClass.java
class DemoThread1 extends Thread {
public DemoThread1(String n) {
super(n);
setPriority(MAX_PRIORITY);
}
public void run() {
System.out.println(getName() + " is running");
}
}
class DemoThread2 extends Thread {
@Override
public void run() {
int cnt = 1;
while (true) {
System.out.print(cnt + " ");
cnt++;
if (cnt > 6) break;
try { Thread.sleep(100); } catch (InterruptedException ex) { System.out.println(ex); }
}
}
}
public class DemoClass {
public static void main(String[] argvs) throws InterruptedException {
DemoThread1 th = new DemoThread1("first Thread ");
System.out.println("Thread id: " + th.getId());
System.out.println("Thread name: " + th.getName());
System.out.println("Thread priority: " + th.getPriority());
th.start();
System.out.println("Thread State: " + th.getState());
System.out.println("Thread alive: " + th.isAlive());
DemoThread2 th2 = new DemoThread2();
try { Thread.sleep(100); } catch (Exception ex) {}
th2.setDaemon(true);
th2.start();
th.join();
th2.join();
}
}
Deadlock Scenario
A deadlock occurs when two or more threads are blocked indefinitely, waiting for each other to release the resources that they need. This example demonstrates a deadlock situation using synchronized blocks.
ThreadSynchronization.java
public class ThreadSynchronization {
public static void main(String argvs[]) throws InterruptedException {
Object ob1 = new Object();
Object ob2 = new Object();
Object ob3 = new Object();
Thread th1 = new Thread(new SynThread(ob1, ob2), "th1");
Thread th2 = new Thread(new SynThread(ob2, ob3), "th2");
Thread th3 = new Thread(new SynThread(ob3, ob1), "th3");
th1.start();
Thread.sleep(500);
th2.start();
Thread.sleep(500);
th3.start();
}
}
class SynThread implements Runnable {
private Object ob1;
private Object ob2;
public SynThread(Object ob1, Object ob2) {
this.ob1 = ob1;
this.ob2 = ob2;
}
@Override
public void run() {
String strName = Thread.currentThread().getName();
System.out.println(strName + " is taking control on the lock " + ob1);
synchronized (ob1) {
System.out.println(strName + " acquired the lock on " + ob1);
work();
System.out.println(strName + " iz taking control on the lock " + ob2);
synchronized (ob2) {
System.out.println(strName + " acquired the lock on " + ob2);
work();
}
System.out.println(strName + " released the lock on " + ob2);
}
System.out.println(strName + " released the lock on " + ob1);
System.out.println(strName + " finished the execution.");
}
private void work() {
try { Thread.sleep(40000); } catch (InterruptedException ie) { ie.printStackTrace(); }
}
}
Solving a Sudoku Puzzle
Java Code
public class SudokuSolver {
private static final int SIZE = 9;
// Function to solve the Sudoku
public boolean solveSudoku(int[][] board) {
for (int row = 0; row < SIZE; row++) {
for (int col = 0; col < SIZE; col++) {
if (board[row][col] == 0) { // Empty cell
for (int num = 1; num <= SIZE; num++) {
if (isSafe(board, row, col, num)) {
board[row][col] = num;
if (solveSudoku(board)) {
return true;
}
board[row][col] = 0; // Backtrack
}
}
return false;
}
}
}
return true; // Solved
}
// Function to check if it's safe to place a number
private boolean isSafe(int[][] board, int row, int col, int num) {
// Check row and column
for (int i = 0; i < SIZE; i++) {
if (board[row][i] == num || board[i][col] == num) {
return false;
}
}
// Check 3x3 subgrid
int startRow = row - row % 3;
int startCol = col - col % 3;
for (int i = startRow; i < startRow + 3; i++) {
for (int j = startCol; j < startCol + 3; j++) {
if (board[i][j] == num) {
return false;
}
}
}
return true;
}
// Utility function to print the board
public void printBoard(int[][] board) {
for (int[] row : board) {
for (int num : row) {
System.out.print(num + " ");
}
System.out.println();
}
}
public static void main(String[] args) {
int[][] board = {
{5, 3, 0, 0, 7, 0, 0, 0, 0},
{6, 0, 0, 1, 9, 5, 0, 0, 0},
{0, 9, 8, 0, 0, 0, 0, 6, 0},
{8, 0, 0, 0, 6, 0, 0, 0, 3},
{4, 0, 0, 8, 0, 3, 0, 0, 1},
{7, 0, 0, 0, 2, 0, 0, 0, 6},
{0, 6, 0, 0, 0, 0, 2, 8, 0},
{0, 0, 0, 4, 1, 9, 0, 0, 5},
{0, 0, 0, 0, 8, 0, 0, 7, 9}
};
SudokuSolver solver = new SudokuSolver();
if (solver.solveSudoku(board)) {
System.out.println("Solved Sudoku:");
solver.printBoard(board);
} else {
System.out.println("No solution exists.");
}
}
}
Algorithm Analysis
- Time Complexity: O(9^(n*n)) in the worst case, where n is the grid size (9 for a standard Sudoku). Each empty cell can take one of 9 values.
- Space Complexity: O(n*n) for the grid, plus recursion stack space for backtracking.
Java Coding Interview Questions (MCQ)
Test your understanding of Java data structures and algorithms with these multiple-choice questions:
Question 1: What is the time complexity of searching in a HashMap?
Answer: O(1). HashMap provides constant-time complexity for search operations in average cases due to hashing.
Question 2: Which of the following is true about TreeMap compared to HashMap?
Answer: TreeMap maintains natural order. It is implemented using a Red-Black Tree and keeps entries sorted.
Question 3: What is a key advantage of interfaces in Java?
Answer: Allows multiple inheritance. Interfaces enable Java to achieve multiple inheritance by allowing a class to implement multiple interfaces.
Java Design Patterns and Keywords: Multiple Choice Questions
This section presents multiple-choice questions (MCQs) focusing on Java design patterns and keywords, along with explanations of the correct answers.
Design Pattern for Hiding Instantiation Logic
Which design pattern is best suited for creating objects without directly exposing the instantiation logic to the client (the code that uses the objects)?
- Singleton
- Factory Method
- Prototype
- Builder
Answer
B: Factory Method
The Factory Method pattern provides an interface for creating objects, but the actual object creation is delegated to subclasses. This hides the specific instantiation logic from the client code.
Purpose of the volatile
Keyword
In Java, what is the primary purpose of the volatile
keyword when applied to a variable?
- To synchronize access to a shared resource (using locks).
- To prevent a class from being extended (subclassed).
- To indicate that a variable's value might change asynchronously by another thread.
- To enforce the creation of only one instance of a class (Singleton pattern).
Answer
C: To specify that a variable may be changed asynchronously.
The volatile
keyword ensures that changes to a variable are immediately visible to all threads, preventing inconsistencies that can occur in multi-threaded environments where a thread might be working with a stale value.