trying CP in Java


using java for CP

i like java more than cpp and its messy style of writing code , java is more intentional clean and just the way i like it this is a translation of most of the cpp techniques used in competitive programming to java and how to use them effectively in java

warning

I in no way is a good competitive programmer , i just do leetcode so that i can crack a job interview someday , i do not have the IQ or mental strength required to be a competitive programmer this template was made just so that i could get a better grasp of java as its my language of choice to do leetcode in

if you can understand CPP code , and is okay with its hedious syntax then you should just use it in Java its almost always slower than cpp so if u are serious about competitive programming and want to win contests then you should just use cpp I am just a guy using java for sake of cracking job interviews

the code below has java 16+ syntax so heads up if u use java 8 in 2026

java template

import java.util.*;

public class  Main {
    public static void main(String[] args) {
            // your code here
    }
}

compiling

javac Main.java

running

java Main

fast input output

the scanner class is too slow for cp we can make our own fast input and output class in our template to make it faster

import java.io.*;
import java.util.*;

class FastIO {
    BufferedReader reader;
    StringTokenizer tokenizer;
    public FastIO() {
        reader = new BufferedReader(new InputStreamReader(System.in));
    }
    String next() {
         while (tokenizer == null || !tokenizer.hasMoreTokens()){
            try {
                tokenizer = new StringTokenizer(reader.readLine());
            } catch (IOException e) {
               e.printStackTrace();
            }
        }
        return s.nextToken();
    }

    int nextInt() {
        return Integer.parseInt(next());
    }
    int nextLong() {
         return Long.parseLong(next());
    }
    double nextDouble() {
        return Double.parseDouble(next());
    }
    String nextLine() {
        String str = "";
        try {
            str = reader.readLine();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

well for most leetcode problems a fastreder isnt necessary

basic algorithm

how to find maximum subarray sum in java

O(n) solution

public int maxSubArray(int[] nums) {
    int maxSum = Integer.MIN_VALUE;
    int currentSum = 0;
    for (int num : nums ) {
        currentSum = Math.max(num, currentSum + num);
        maxSum = Math.max(maxSum, currentSum);
    }
    return maxSum;

this was just an example in cses handp for teaching time complexity but anyways i thought i’d include it here

sorting in java

sorting normal arrays using Arrays.sort() method

import java.util.Arrays;


int[] arr = {5, 2, 8, 1, 3};
Arrays.sort(arr);

reverse sorting

sorting in reverse order can be done using comparators Collections provide a good comparator for reverse order sorting but it only works for boxed types like Integer and not for primitive types like int so we need to use Integer[] instead of int[] for reverse sorting

import java.util.Arrays;
import java.util.Collections;

Integer[] arr = {5, 2, 8, 1, 3};
Arrays.sort(arr, Collections.reverseOrder());

another way to do it is to use StreamsAPI

int arr[] = {5,2,8,1,3};
arr = Arrays.stream(arr).boxed().sorted(Collections.reverseOrder()).mapToInt(Integer::intValue).toArray();

we first convert the arrays to a stream then box the primitive int to Integer then sort it in reverse order using the comparator and then unbox it back to int array

if it was already an Integer array we can just do

Integer[] arr = {5, 2, 8, 1, 3};
arr = Arrays.stream(arr).sorted(Collections.reverseOrder()).toArray(Integer[]::new);

sorting ArrayList

we can use sort() method of the arrayList to sort it

import java.util.ArrayList;

List<Integer> list = new ArrayList<>();
list.add(5);
list.add(2);
list.add(8);
list.add(1);
list.add(3);

list.sort(null); // sorts in natural order 

to sort in reverse order we just use a comparator

list.sort(Collections.reverseOrder());

since list is already Integer we can directly use the comparator without needing to box or unbox anything

comparators

some cases we need to sort object based on some of their attributes for example if we have a class Person with attributes name and age and we want to sort a list of people based on their age we can use a comparator a person class and we need to sort them by age we can just use a custom comparator for that

class Person {
    String name;
    int age;
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
    public int getAge() {
        return age;
    }
}

List<Person> people = new ArrayList<>();
people.add(new Person("Alice", 30));
people.add(new Person("Bob", 25));
people.add(new Person("Charlie", 35));

people.sort((a,b)->Integer.compare(a.age,b.age)); // sorts by age in ascending order 

to sort in reverse order we can just swap a and b in the comparator

people.sort((a,b)->Integer.compare(b.age,a.age); // sorts by age in descending order 

a cleaner way to do it is to use the Comparator.comparingInt() method which is a static method that takes a function that extracts the int value from the object and returns a comparator that compares the int values

people.sort(Comparator.comparingInt(Person::getAge)); // uses the getAge method to extract the age and sort by it in ascending order 

people.sort(Comparator.comparingInt(p->p.age)); // same as above but using a lambda expression instead of method reference

// descending order
people.sort(Comparator.comparingInt(Person::getAge).reversed()); // sorts by age

people.sort(Comparator.comparingInt(p->p.age).reversed()); // same as above but using a lambda expression instead of method reference

multistep sorting

Comparator API allows for mutlistep sorting using thenComparing() method which is a default method that takes another comparator and returns a new comparator that first compares using the original comparator and if they are equal then compares using the second comparator

people.sort(Comparator.comparingInt(Person::getAge).thenComparing(p->p.name)); // sorts by age in ascending order and if ages are equal then sorts by name in ascending order

these comaprator themselves can nest other comparators for example if we have another attribute height in the person class and we want to sort by age then by height then by name we can do

class Person {
    String name;
    int age;
    int height;
    public Person(String name, int age, int height) {
        this.name = name;
        this.age = age;
        this.height = height;
    }
}

people.sort(Comparator.comparingInt(p->p.age).thenComparing(Comparator.comparingInt(p->p.height)).thenComparing(p->p.name)); // sorts by age then by height then by name in ascending order

sorting with streams

non destructive sorting can be done using streams

List<Integer> sortedList = list.steam().sorted().toList();

sorting Strings

lets first discuss how to sort an array of strings

String[] arr = {"banana", "apple", "cherry"};
Arrays.sort(arr);

this will sort the array in lexicographical order

to sort in reverse order we can use the same technique as before

String[] arr = {"banana", "apple", "cherry"};
Arrays.sort(arr, Collections.reverseOrder());

arrayList of strings can be sorted using the same technique as before

List<String> list = new ArrayList<>();
list.add("banana");
list.add("apple");
list.add("cherry");

list.sort(null); // sorts in natural order

to sort in reverse order

list.sort(Collections.reverseOrder());

to sort case insensitive we can use a custom comparator

list.sort(String.CASE_INSENSITIVE_ORDER); // sorts in case insensitive order

we can also just call reversed method on the comparator to sort in reverse case insensitive order

list.sort(String.CASE_INSENSITIVE_ORDER.reversed()); // sorts in reverse case insensitive order

sorting a string

easiest way to do it is to convert the string to a char array then sort it and convert it back to stirng

String str = "banana";
char[] arr = str.toCharArray();
Arrays.sort(arr);
String sortedStr = new String(arr);

however to sort it in reverse order we can use a custom comparator for the char array

String str = "banana";
char[] arr = str.toCharArray();
Arrays.sort(arr);
String sortedStr = new StringBuilder(new String(arr)).reverse().toString();

we reverse the ascending sorted string to get the descending sorted string time saver apporach


String sortedStr = Stream.of(str.split("")).sorted(Comparator.reverseOrder()).collect(Collectors.joining());

comparison in list of arrays

sometimes we wanna store [x,y] arrays in a list like list of all points and wants them sorted in a way that it sorts by x first if not then y is checked

u can do this with comparator chaining

points.sort(Comparator.comparingInt(point->point.x).thenComparing(Comparator.comparingInt(point->point.y)))

in cpp we have pair class that automatically implements it but in java we recommend using meanigful names we can implement a pair class if we want

record Pair(int first , int second) implements Comparable<Point> {
        @Override
        public int compareTo(Pair other){
                if(this.first != other.first){
                     return Integer.compare(this.first,other.first);
                }
                return Integer.compare(this.second,other.second);
        }

}

or we can just write the comparator we used

record Pair(int first,int second) implements Comparable<Pair>{
    @Override
    public int compareTo(Pair other){
            return Comparator.comparingInt(Pair::first)
                         .thenComparingInt(Pair::second)
                         .compare(this, other);
    }
}

for tuple like behaviour in cpp where one comparison chained by next element lexicographical comparison of list we

we can easily extend this

record Tuple(int first,int second,int third) implements Comparable<Tuple>{
        @Override
        public int Tuple(Tuple second){
            return Comparator.comparingInt(Tuple::first)
                         .thenComparingInt(Tuple::second)
                         .thenComparingInt(Tuple::third)
                         .compare(this, other);
        }
}

this can be sorted by

ls.sort(null)
Collections.sort(ls)

binary Search in java

binary search can be done using Arrays.binarySearch() method for arrays and Collections.binarySearch() method for lists

int[] arr = {1, 2, 3, 4, 5};
int index = Arrays.binarySearch(arr, 3); // returns the index of the element if
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
int index = Collections.binarySearch(list, 3); // returns the index of the element if

we also get the insertion point if the element is not found

int index = Arrays.binarySearch(arr, 6); // returns -6 which means the element is not found and can be inserted at index 5
index = Math.abs(index+1); // to get the insertion point we take the absolute value of the index and subtract 1 from it

first lower bound gives the first element in the array that is atleast the target value and first upper bound gives the first element in the array that is greater than the target value

there is no builtin method for this but we can get this using a treeSet

TreeSet<Integer> set = new TreeSet<>();
set.add(1);
set.add(2);
set.add(3);

set.ceiling(2); // returns 2 which is the first element that is atleast 2
set.higher(2); // returns 3 which is the first element that is greater than

tree map also provides set.floor() and set.lower() methods which return the first element that is at most the target value and the first element that is less than the target value respectively

to convert an array or arraylist into treeset its pretty easy

// for array
int[] arr = {1, 2, 3, 4, 5};
TreeSet<Integer> set = new TreeSet<>(Arrays.stream(arr).boxed().toList());
// for arraylist
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
TreeSet<Integer> set = new TreeSet<>(list);

treeSet is internally using a Red Black tree which is a self balancing binary search tree so all the operations like add, remove, ceiling, higher, floor, lower etc are all O(log n) time complexity treeSet relies on the treeMap for its implementation so all the operations are O(log n) time complexity and it also maintains the elements in sorted order so we can use it for binary search and also for getting the first element that is atleast or greater than the target value using ceiling and higher methods respectively

data structures in java

array lists

dynamic Arrays or ArrayList in java is a resizable array that can grow and shrink in size as needed

List<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
list.add(3);

to access the elements in the arraylist we can use the get() method

int firstElement = list.get(0); // returns 1

to print an arraylist we can use the toString() method

System.out.println(list); // prints [1, 2, 3]

to remove the last element from the arraylist we can use the remove() method

list.remove(list.size() - 1); // removes the last element from the list

to initialize an arraylist with a specific size we can use the constructor that takes the initial capacity as an argument

List<Integer> list = new ArrayList<>(100); // initializes an arraylist with an initial capacity of 100

what about initializeing all elemets with a certain value

List<Integer> list = new ArrayList<>(Collections.nCopies(100, 0)); // initializes an arraylist with 100 elements all initialized to 0

to convert an array to an arraylist we can use the Arrays.asList() method

Integer[] arr = {1, 2, 3, 4, 5};
List<Integer> list = Arrays.asList(arr); // converts the array to an arraylist

to convert an arraylist to an array we can use the toArray() method

List<Integer> list = Arrays.asList(1, 2, 3, 4)
Integer[] arr = list.toArray(new Integer[0]); // converts the arraylist to an array

strings

we can use the String class in java to work with strings

String str = "Hello, World!";

to get the length of the string we can use the length() method

int length = str.length(); // returns 13

to get a character at a specific index we can use the charAt() method

char firstChar = str.charAt(0); // returns 'H'

to concatenate two strings we can use the concat() method or the + operator

String str1 = "Hello, ";
String str2 = "World!";
String result = str1.concat(str2); // returns "Hello, World!"
String result2 = str1 + str2; // also returns "Hello, World!"

we can convert a string to a char array using the toCharArray() method

we can get a substring of a string using the substring() method

String str = "Hello, World!";
String subStr = str.substring(0, 5); // returns "Hello"

to split a string into an array of substrings we can use the split() method

String str = "Hello, World!";
String[] parts = str.split(", "); // returns ["Hello", "World!"]

Sets

treeSets

whenver you want a tree based ordered Set we can rely on treeSet relies on tree so uses O(log n)

TreeSet<Integer> ts = new TreeSet<>();

tree set elements are distinct , u cant have duplicates in it

ts.add(3);
ts.add(2);
ts.add(5);
ts.remove(4);

it also has methods to remove first element and last element

ts.pollFirst();
ts.pollLast();

to remove all elements that matches a condition

ts.removeIf(x->x>25)

hashSet

this is unordered set , but its operations are O(1)

HashSet<Integer> set = new HashSet<>();
set.add(4);
set.add(10);

set.contains(4); // boolean to check 

set.remove(10);

set.size();
set.clear();

search isnt possible with this

java Sets doesnt allow duplicates, trying to add a duplicate element will not throw an error but it will simply ignore the duplicate and the set will remain unchanged.

Sets that allow multiple occurrences of the same element great for tracking counts of elements , and also retaining lowerbound upperbound functionality of treeSet but with duplicates it also has unorderd version of it unordered_multiset which is based on hashSet and has O(1) operations but no order , which is very convienient for counting elements fast in java we can implement it with maps (more verbose ) well see more about maps in next section


HashMap<Integer, Integer> multiSet = new HashMap<>(); 
multiSet.put(1, multiSet.getOrDefault(1, 0) + 1); // add 1 to the count of element 1
multiSet.put(2, multiSet.getOrDefault(2, 0) + 1); // add 1 to the count of element 2
multiSet.put(1, multiSet.getOrDefault(1, 0) + 1); // add 1 to the count of element 1 again


// to get the count of any element
int countOf1 = multiSet.getOrDefault(1, 0); // returns 2

// to remove an element we can just decrease its count
multiSet.put(1, multiSet.getOrDefault(1, 0) - 1); // removes one occurrence of element 1

if you want the properties of a treeSet with duplicates you can use a TreeMap to implement it

TreeMap<Integer, Integer> multiSet = new TreeMap<>();
multiSet.put(1, multiSet.getOrDefault(1, 0) + 1); // add 1 to the count of element 1
multiSet.put(2, multiSet.getOrDefault(2, 0) + 1); // add 1
multiSet.put(1, multiSet.getOrDefault(1, 0) + 1); // add 1 to the count of element 1 again

// to get the count of any element
int countOf1 = multiSet.getOrDefault(1, 0); // returns

// to remove an element we can just decrease its count
multiSet.put(1, multiSet.getOrDefault(1, 0) - 1); // removes one occurrence of element 1

In my personal opinion javas explicit way of implementing a muliset is better than cpps implicit way of doing it with a multiset using map to count things is such a common technique in coding

Maps

maps are the underlying data structure behind the hashSet and treeSet so they have the same time complexity for their operations

hashMap

HashMap<String, Integer> map = new HashMap<>();
map.put("apple", 1);
map.put("banana", 2);
map.get("apple"); // returns 1
map.containsKey("banana"); // returns true
map.remove("apple");

treeMap

TreeMap<String, Integer> map = new TreeMap<>();
map.put("apple", 1);
map.put("banana", 2);
map.get("apple"); // returns 1
map.containsKey("banana"); // returns true
map.remove("apple");

treemaps are self balancing binary search trees so they maintain the keys in sorted order and also provide methods for getting the first key, last key, ceiling key, floor key etc which are not available in hashMap

map.firstKey(); // returns the first key in the map
map.lastKey(); // returns the last key in the map
map.ceilingKey("avocado"); // returns the least key greater than or equal to "
map.floorKey("avocado"); // returns the greatest key less than or equal to "avocado"

hashmaps are faster than treemaps for most operations but treemaps provide more functionality and maintain the keys in sorted order so they are useful when we need to maintain the order of the keys or when we need to perform range queries on the keys

to print all key values in a map we can use the entrySet() method which returns a set of key value pairs and we can iterate over it using a for each loop

for (Map.Entry<String, Integer> entry : map.entrySet()) {
    System.out.println(entry.getKey() + ": " + entry.getValue());
}

to iterate over the keys in a map we can use the keySet() method which returns a set of keys and we can iterate over it using a for each loop

for (String key : map.keySet()) {
    System.out.println(key);
}

to iterate over the values in a map we can use the values() method which returns a collection of values and we can iterate over it using a for each loop

for (Integer value : map.values()) {
    System.out.println(value);
}

other operations commonly performed are

map.getOrDefault("orange", 0); // returns the value associated with "orange" if it exists, otherwise returns 0
map.putIfAbsent("grape", 3); // puts the key "grape" with value 3 if it is not already present in the map, otherwise does nothing
map.replace("banana", 4); // replaces the value associated with "banana" with 4 if it exists 
map.replace("banana", 2, 5); // replaces the value associated with "banana" with 5 if it is currently 2, otherwise does nothing

Iterators in java

iterators are used to iterate over the elements in a collection like a list, set or map

List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
Iterator<Integer> iterator = list.iterator();

while (iterator.hasNext()) {
    Integer element = iterator.next();
    System.out.println(element);
}

some useful mehtods from collections

Collections.reverse(list); // reverses the order of the elements in the list 
Collections.shuffle(list); // randomly shuffles the elements in the list

//with seed 
Collections.shuffle(list, new Random(seed)); // randomly shuffles the elements in the list using a specific seed for reproducibility

we can use sublist method to get a sublist of a list

List<Integer> subList = list.subList(1, 4); // returns a view of the portion of the list between the specified fromIndex (inclusive) and toIndex (exclusive)

iterating over a sublist

for (Integer element : list.subList(1, 4)) {
    System.out.println(element);
}

set iterators

in java sets can be used as queues and stacks most of them provide methods to get the first and last element in the set respectively

TreeSet<Integer> set = new TreeSet<>();
set.add(1);
set.add(2);
set.add(3);

set.first(); // returns 1
set.last(); // returns 3

iterating from the first element to the last element

Iterator<Integer> iterator = set.iterator();
while (iterator.hasNext()) {
    Integer element = iterator.next();
    System.out.println(element);
}

iterating from the last element to the first element

Iterator<Integer> iterator = set.descendingIterator();
while (iterator.hasNext()) {
    Integer element = iterator.next();
    System.out.println(element);
}

iterating from a specific element to the last element

Iterator<Integer> iterator = set.tailSet(2).iterator();
while (iterator.hasNext()) {
    Integer element = iterator.next();
    System.out.println(element);
}

tailset returns a view of the portion of the set whose elements are greater than or equal to the specified element iterating from a specific element to the first element

Iterator<Integer> iterator = set.headSet(2, true).descendingIterator();
while (iterator.hasNext()) {
    Integer element = iterator.next();
    System.out.println(element);
}

headset returns a view of the portion of the set whose elements are less than (or equal to if the second argument is true) the specified element

BitSets

in java java.utils.BitSet class provides a way to represent a set of bits or boolean values in a compact and efficient way

BitSet bitSet = new BitSet(1001010);
bitSet.set(0); // sets the bit at index 0 to true
bitSet.set(2); // sets the bit at index 2 to true
bitSet.get(0); // returns true 
bitSet.flip(1); // flips the bit at index 1 (if it was false it becomes true and if it was true it becomes false)
bitSet.clear(2); // sets the bit at index 2 to false
bitSet.nextSetBit(0); // returns the index of the next set bit (true bit) starting from index 0
bitSet.nextClearBit(0); // returns the index of the next clear bit (false bit) starting from index 0

bit sets also provide methods for performing bitwise operations like and, or, xor etc

BitSet bitSet1 = new BitSet(1001010);
bitSet1.set(0);
//.... sets the bits in bitSet1 
BitSet bitSet2 = new BitSet(1010101);
// sets the bits in bitSet2
bitSet1.xor(bitSet2); // performs a bitwise exclusive OR operation on the two bit sets and stores the result in bitSet1
bitSet1.and(bitSet2); // performs a bitwise AND operation on the two bit
bitSet1.or(bitSet2); // performs a bitwise OR operation on the two bit sets and stores the result in bitSet1

initialiing a bit set with a binary string

String binaryString = "1001010";
BitSet bitSet = new BitSet(binaryString.length());
for (int i = 0; i < binaryString.length(); i++) {
    if (binaryString.charAt(i) == '1') {
        bitSet.set(i);
    }
}

initializing a bit set with a long value

long value = 1001010L;
BitSet bitSet = BitSet.valueOf(new long[]{value});

or more concisely

BitSet bitSet = BitSet.valueOf(new long[]{1001010L});

DeQueue

java has Dequeu interface which is implemented by ArrayDeque class

Deque<Integer> deque = new ArrayDeque<>();
deque.addFirst(1); // adds 1 to the front of the deque
deque.addLast(2); // adds 2 to the back of the deque
deque.getFirst(); // returns 1
deque.getLast(); // returns 2
deque.removeFirst(); // removes and returns the first element of the deque
deque.removeLast(); // removes and returns the last element of the deque
deque.pollFirst(); // removes and returns the first element of the deque or returns null if the deque is empty
deque.pollLast(); // removes and returns the last element of the deque or returns null if the deque is empty
deque.isEmpty(); // returns true if the deque is empty, false otherwise
deque.size(); // returns the number of elements in the deque

it can also be implemented by a linked list using the LinkedList class which also implements the Deque interface

Deque<Integer> deque = new LinkedList<>();
deque.addFirst(1); // adds 1 to the front of the deque
deque.addLast(2); // adds 2 to the back of the deque
deque.getFirst(); // returns 1
deque.getLast(); // returns 2
deque.removeFirst(); // removes and returns the first element of the deque
deque.removeLast(); // removes and returns the last element of the deque
deque.pollFirst(); // removes and returns the first element of the deque or returns null if the deque is empty
deque.pollLast(); // removes and returns the last element of the deque or returns null if the
deque.isEmpty(); // returns true if the deque is empty, false otherwise 
deque.size(); // returns the number of elements in the deque

linked list is slower tan arrayDeque

Stacks

stack interface is implemented by the Stack class which is based on a vector and also by the ArrayDeque class which is based on a resizable array

Stack<Integer> stack = new Stack<>();
stack.push(1); // pushes 1 onto the stack
stack.push(2); // pushes 2 onto the stack
stack.peek(); // returns the top element of the stack without removing it (returns 2)
stack.pop(); // removes and returns the top element of the stack (returns 2)
stack.isEmpty(); // returns true if the stack is empty, false otherwise
stack.size(); // returns the number of elements in the stack

arrayDeque can also be used as a stack by using the push, peek and pop methods
Deque<Integer> stack = new ArrayDeque<>();
stack.push(1); // pushes 1 onto the stack
stack.push(2); // pushes 2 onto the stack
stack.peek(); // returns the top element of the stack without removing it (returns 2)
stack.pop(); // removes and returns the top element of the stack (returns 2)
stack.isEmpty(); // returns true if the stack is empty, false otherwise
stack.size(); // returns the number of elements in the stack

Queue interface is implemented by the LinkedList class and also by the ArrayDeque class

Queue<Integer> queue = new LinkedList<>();
queue.add(1); // adds 1 to the back of the queue
queue.add(2); // adds 2 to the back of the queue
queue.peek(); // returns the front element of the queue without removing it (returns 1)
queue.poll(); // removes and returns the front element of the queue (returns 1)
queue.isEmpty(); // returns true if the queue is empty, false otherwise
queue.offer(3); // adds 3 to the back of the queue and returns true if the element was added successfully, false otherwise
Queue<Integer> queue = new ArrayDeque<>();
queue.add(1); // adds 1 to the back of the queue
queue.add(2); // adds 2 to the back of the queue
queue.peek(); // returns the front element of the queue without removing it (returns 1)
queue.poll(); // removes and returns the front element of the queue (returns 1)
queue.isEmpty(); // returns true if the queue is empty, false otherwise
queue.offer(3); // adds 3 to the back of the queue and returns true if the element was added successfully, false otherwise 

priorityQueue

implemented by the PriorityQueue class which is based on a binary heap

PriorityQueue<Integer> pq = new PriorityQueue<>();
pq.add(3); // adds 3 to the priority queue
pq.add(1); // adds 1 to the priority queue
pq.add(2); // adds 2 to the priority queue
pq.remove(2); // removes 2 from the priority queue
pq.peek(); // returns the smallest element in the priority queue without removing it (returns 1)
pq.poll(); // removes and returns the smallest element in the priority queue (returns 1)
pq.isEmpty(); // returns true if the priority queue is empty, false otherwise
pq.size(); // returns the number of elements in the priority queue

using custom comparators with priority queues

PriorityQueue<Integer> pq = new PriorityQueue<>(Comparator.reverseOrder()); // creates a max heap
pq.add(3); // adds 3 to the priority queue
pq.add(1); // adds 1 to the priority queue
pq.add(2); // adds 2 to the priority queue
pq.peek(); // returns the largest element in the priority queue without removing it (returns 3
pq.poll(); // removes and returns the largest element in the priority queue (returns 3)

final CP template

well i AI generated a template that would cover for most loses in competitive programming and also has a lot of the techniques we discussed in this article

import java.util.*;
import java.io.*;

public class Main {
    static FastReader in = new FastReader();
    static PrintWriter out = new PrintWriter(System.out);

    // --- CORE SOLVE LOGIC ---
    public static void solve(int tCase) {
        // Your logic for each test case goes here
        // Example: 
        // int n = in.nextInt();
        // out.println("Case " + tCase + ": " + n);
    }

    public static void main(String[] args) {
        // Read number of test cases
        int t = 1; 
        try {
            String next = in.next();
            if (next != null) {
                t = Integer.parseInt(next);
            }
        } catch (Exception e) {
            t = 1; // Default to 1 if input is weird
        }

        for (int i = 1; i <= t; i++) {
            solve(i);
        }
        
        out.flush();
        out.close();
    }

    // --- 1. FENWICK TREE (Order Statistics / Prefix Sums) ---
    static class FenwickTree {
        int[] tree;
        int n;
        FenwickTree(int n) { this.n = n; this.tree = new int[n + 1]; }
        
        void update(int i, int val) {
            for (; i <= n; i += i & -i) tree[i] += val;
        }
        
        int query(int i) {
            int sum = 0;
            for (; i > 0; i -= i & -i) sum += tree[i];
            return sum;
        }

        int findKth(int k) {
            int idx = 0, total = 0;
            for (int i = 1 << (31 - Integer.numberOfLeadingZeros(n)); i > 0; i >>= 1) {
                if (idx + i <= n && total + tree[idx + i] < k) {
                    idx += i;
                    total += tree[idx];
                }
            }
            return idx + 1;
        }
    }

    // --- 2. PAIR (Record for Immutability & Auto Equals/Hash) ---
    record Pair<A extends Comparable<A>, B extends Comparable<B>>(A first, B second) 
        implements Comparable<Pair<A, B>> {
        public int compareTo(Pair<A, B> other) {
            int cmp = this.first.compareTo(other.first);
            return cmp != 0 ? cmp : this.second.compareTo(other.second);
        }
    }

    // --- 3. MULTISET (Sorted duplicates) ---
    static class MultiSet<T extends Comparable<T>> {
        TreeMap<T, Integer> map = new TreeMap<>();
        int size = 0;
        void add(T x) { map.put(x, map.getOrDefault(x, 0) + 1); size++; }
        void remove(T x) {
            if (!map.containsKey(x)) return;
            int cnt = map.get(x);
            if (cnt == 1) map.remove(x); else map.put(x, cnt - 1);
            size--;
        }
        int count(T x) { return map.getOrDefault(x, 0); }
        T first() { return map.firstKey(); }
        T last() { return map.lastKey(); }
    }

    // --- 4. FAST SORT (Avoids O(N^2) Anti-testcases) ---
    static void shuffleSort(int[] a) {
        Random rnd = new Random();
        for (int i = 0; i < a.length; i++) {
            int j = rnd.nextInt(a.length), t = a[i]; a[i] = a[j]; a[j] = t;
        }
        Arrays.sort(a);
    }

    // --- 5. FAST I/O ---
    static class FastReader {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringTokenizer st = new StringTokenizer("");
        String next() {
            while (st == null || !st.hasMoreElements()) {
                try {
                    String line = br.readLine();
                    if (line == null) return null;
                    st = new StringTokenizer(line);
                } catch (IOException e) { return null; }
            }
            return st.nextToken();
        }
        int nextInt() { return Integer.parseInt(next()); }
        long nextLong() { return Long.parseLong(next()); }
        double nextDouble() { return Double.parseDouble(next()); }
    }
}

this is a bit overkill