Skip to content

Instantly share code, notes, and snippets.

@xobe19
Last active October 4, 2023 01:25
Show Gist options
  • Save xobe19/aac5802bcb5292f9f9df79e2e73adfa7 to your computer and use it in GitHub Desktop.
Save xobe19/aac5802bcb5292f9f9df79e2e73adfa7 to your computer and use it in GitHub Desktop.
Java 8

Compilation

  1. Source Code: .java file -> Compiled to .class file (bytecode) using javac
  2. This .class file can only be run by a JVM unlike the ./a.out files in C/C++

Entry Point

  1. Each .java file is only allowed to have a single class that matches the name of the file
  2. Only one public class allowed per file and the name of this public class is the name of the file
  3. The main() method of this class is the entry point of the execution

Packages

  1. Java needs to have packages because you can't import from "path" (e.g, import libA from "./myLib") like you do in JS
  2. The folders are literally packages, without any extra setup
  3. Folder structure: "folderA/folderB/myFile.java"
  4. How to import: import folderA.folderB.myFile

JShell

  1. Made up of snippets, ( each statement is a snippet )
  2. Can execute code directly without making classes for everything
  3. A snippet is a: statement, class declaration, method declaration , etc
  4. Each snippet has an identified $12 , $13, etc
  5. Any snippet written in past can be edited by either re-declaring the method / class contents or by using the /edit id command where id is the $id identifier

Fields vs Variables

  1. Fields are class properties, two types of fields
    1. Non Static Fields ( instance variables )
    2. Static Fields ( Class Variables )
  2. Variables are:
    1. **Local Variables ( the variables used inside methods ) **
    2. Method Parameters

Primitive Data Types

  1. byte (8 bits)
  2. short ( 16 bits )
  3. int ( 32 bits )
  4. long (64 bits)
  5. float ( IEEE 754 , 32 bits )
  6. double ( IEEE 754, 64 Bits )
  7. boolean
  8. char (16 bit Unicode)

Primitive Data types cannot be set to NULL

Literals for int, long, float, double: I, L, F, D Declaring hex and binary variables: 0xFD126, 0b10111 Numbers can be seperated by _

Arrays

int[] array

int[] is the array data type

Initialization

array = new int[10];

int[] arr = {1,2,3,4};

Methods

Use the java.util.Arrays class extensively for methods to manipulate arrays

  1. java.util.Arrays.binarySearch(int[] a, int key) -> returns index otherwise (-(insertion point) - 1) where insertion point is the index of the element where the element WOULD have been present if it was existing

NOTE:

  1. if a = -b - 1 then b = -a - 1

  2. java.util.Arrays.sort()

  3. java.util.Arrays.fill()

  4. java.util.Arrays.toString(): prints array as a string

  5. java.util.Arrays.asList() to convert array to list

InstanceOf

  1. Checks if a given object is an instance of a class, sub class or an extending interface

BitShifting

  1. Left Shift: Moves bits to left and fills empty places with 0
  2. Right Shift (>>): Moves bits to right and
    1. Fills empty spaces with 0 if it's a positive number
    2. FIlls empty spaces with 1 if it's a negative number
  3. Right Shift ( >>>): Fills empty spaces with 0 no matter what

Labelling in Loops

mylabel: for(int i = 0....)

while breaking: break mylabel; while continuing: continue mylabel;

Classes

Varargs

`public void methodName(Object... params) {

params[0], params[1],.... are all Objects

}

`

  1. Primitives are passed by value
  2. References are also passed by value ( everything is an object/class in java so copying ka no chance)

Creating Objects

Syntax:

this.new ClassName() where this is the context where the class ClassName is defined

Eg, **Nested Class: **

class OuterClass {

class InnerClass {


}
}

To create an instance of the InnerClass first, create an instance of the OuterClass and using that context, create the InnerClass instance

var o = new OuterClass();
var i = o.new InnerClass();

Access Modifiers

![[Pasted image 20230905183245.png]]

Here, Subclass refers to Subclass outside the package

Intuition: Just remember the order from the most lenient access to the most restriced one

These modifiers apply only to the Class Members and as far as class is considered, it only has either public or no modifier

However, since nested classes are technically members of the parent class, they can have public and protected modifiers as well

Static Blocks in Constructor

  1. The intuition behind this is that anything static is something that the class executes Exactly Once and is not dependent on the instance
  2. These static {//code here} blocks are executed in the order that they appear in the code

Encapsulating Class

  1. There are three types of Classes
    1. Nested Class : Class Inside a Class ( Nested class can access outer class's members by using OuterClass.this)
    2. Local Class : Class Inside a Method ( can access outer class in similar way, but the local variables of the method can only be accessed if they're final)

Numbers

  1. Each primitive type has a wrapper class with additional methods

Convert Wrapper to Primitive

byte byteValue() short shortValue() int intValue()

Intuition: since the method name has smallercase int, short, etc it must return the primitive

Convert Primitive to Wrapper

Integer.valueOf(int i) Byte.valueOf(byte i)

Convert String to Wrapper

Integer.valueOf(string i, int radix)

Convert Wrapper to String

Integer a = Integer.valueOf(2); a.toString()

Use the above four to convert from one to another

Comparison and Equality Methods

equals() compareTo()

System.out.printf()

  1. %d for integer
  2. %f for float
  3. %n for newline
  4. precision values are added before the letter
    1. + include the sign
    2. , add commas
    3. '10.3' total 10 letters and point has 3 digit precision

Math Class

  1. Constants like Math.E and Math.PI
  2. Math.abs() , Math.ceil() , Math.floor() return double and take double
  3. Math.round() returns int or long
  4. Math.min() and Math.max()
  5. Math.pow() , Math.log(), Math.exp(), Math.sqrt()
  6. Math.random() double between 0 and 1

Character

  1. Convert a character to a string -> Character.toString(char c)
  2. Character.isLetter()
  3. Character.isDigit()
  4. Character.isLetterOrDigit()
  5. Character.toUpperCase()
  6. Character.isLowerCase()

String

  1. The standard String class is immutable
  2. length using .length() method
  3. get character at index using .charAt() method
  4. get substring by using the subString(int begin, int end) end is not included and if absent, substring is till end of the string
  5. Other methods:
    1. indexOf(string s) , lastIndexOf(string s) , contains(string s)
    2. replace(String old, String new)
    3. endsWith() , startsWith() , compareTo(), equals()

StringBuilder

Modifiable versions of Strings, instead of using the java string pool, they internally use a character array

Convert from String to StringBuilder

1) StringBuilder sb = new StringBuilder(String s)

Convert from StringBuilder to String

  1. sb.toString();

Methods

  1. append() pushes any primitive type or object to the end of the string
  2. delete(int start, int end) deletes these characters (start to end - 1)
  3. deleteCharAt(int index)
  4. insert(int offset, data ) just like append but can insert anywhere in the SB
  5. reverse()
  6. charAt() -> same like the one in String
  7. For replacing and all, convert it to a standard string

Casting

Implicit casting

Class A {}

class B extends A {}
// works
A ob = new B();

This only causes compile-time errors if the assigned type is not correct ( LHS type not matches RHS)

If the (className) cast operator is used, then it tries casting the object at runtime and if it's not working, it just throws a RuntimeError

INTERFACES CAN'T HAVE NORMAL VARIABLES?? MY LIFE IS A LIE

They can only have method signatures, too. If you want to implement a method in interface, you gotta use the default keyword

Why Java doesn't have multiple inheritance?

If java could have inherited multiple classes, then all the fields of the classes would become mixed and the method would not know which field to take the value from

class A {
int a = 4;
void printA() {
println(a);
}
}

class B {
int a = 5;
void printB() {
println(a);
}
}

If some class C could inherit A and B, which field would it use of variable a?

Inheriting multiple interfaces is possible, cuz they only have static/final variables

super confusion

  1. When inheriting from multiple interfaces how to know which super to use from?
  2. ClassName.super.dfsf(), InterfaceName.super etc

Object Methods

  1. the .equals() method in objects compares only the references, i.e, if they're the same object

Generics

  1. Just because Integer is a sub-class of Number doesn't mean that ArrayList<Integer> is a sub-class of ArrayList<Numebr>, they in-fact don't have any relation at all
  2. two types of bounds extends and super
  3. Wildcard parameters are literally the same as using T instead of a ? but the only difference is :
    1. the code is not polluted as ? literally means, be any type idc
    2. it's only possible to use the super bound while using a '?' wildcard'
  4. There is no such thing as a generic type during runtime, it's all converted into normal types and bounds are applied
  5. Can't create an array of parameterized types

Lambdas

  1. They're like quick and easy functions
  2. Java doesn't have the concept of functions, so lambdas are essentially a simpler way of writing a class that has only a single method, this acts as a function
  3. Such interface with a single method is known as @FunctionalInterface

Syntax

(Parameter1 p1, Parameter2 p2...) -> {
// block of code
}

The paranthesis are removable

Comparator Lambda

Comparator<String> c = (s1, s2) -> Integer.compare(s1.length(), s2.length());

Collection Framework

  1. Interface ( the functionality that you need in the collection) on the left side
  2. Class ( the way you need it be implemented ) on the right side
  3. All of the methods like subList, etc that return a new list, only return a view of it, and do not delete actual data from the collection

Available Collections

Interface on left, class on right

  1. List (Interface) -> ArrayList, LinkedList
  2. Set -> HashSet
  3. SortedSet,NavigableSet -> TreeSet
  4. Queue, Deque -> LinkedList
  5. Queue -> Priority Queue
  6. Map -> HashMap, LinkedHashmap
  7. NavigableMap/SortedMap -> TreeMap

Common Methods

Adding/Removing Elements

add(element) remove(element) contains(element)

Set Methods

A.containsAll(B) A.addAll(B) A.removeAll(B) A.retainAll(B)

Size/Empty/Clear

.size() isEmpty() clear()

Lambdas

.removeIf(predicate)

Static Methods

Collections.min() Collections.max() Collections.reverse()

List Methods

add(index, element) get(index) set(index, element) remove(index)

indexOf(element) lastIndexOf(element)

subList(start, end) -> from start to end - 1

listObject.sort(comparator/null)

NavigableSet Methods

  1. prolly don't need to use Set, since sorted set is almost always better
  2. best implementation is using a TreeMap and passing a comparator lambda to it

Methods

  1. first() and last()
  2. headSet(a) and tailSet(b) -> think logically, [HHH------TTT] , the front part (lower elements) is head, and the rearer end (larger) elements is the tail, headSet returns from first element to a but not including a
  3. tailSet returns from b (including b) till the last element
  4. subSet(a, b) returns a combination of a and b
  5. ceiling(element) -> something that is greater than or equal to ele
  6. floor(element) -> something that is less than or equal to ele
  7. higher(element) and lower(element) are the same but without the or equal to
  8. descendingIterator()

Iterator

  1. check hasNext() to see if an element is present, and use next() to get the current pointing element and move to the next pointer

ListIterator

  1. the only thing added is hasPrevious() and previous()
  2. set(element) update the last element returned by next() . i.e, update the element that the iterator is currently pointing to

Updating elements while iterating

  1. probably don't need it since you can use the removeIf() lambda

Queues and Stacks

Note : Queue and Deque are just interfaces, the classes are LinkedList PriorityQueue etc.

If you want to iterate a deque, use .iterator() or .descendingIterator()

Should just use a deque for everything Don't use stack bc it's old ( was released along with Vector and other classes before java 5)

addFirst() addLast() getFirst() getLast() removeFirst() removeLast()

The only time you need to use Queue is when you're using a PriorityQueue

Queue Methods: add() element() remove()

In some questions, Stack might still be asked so remember that the methods are

push() pop() peek() empty()

Map Methods

  1. .put(key, value)
  2. putIfAbsent(key, value)
  3. .values() returns a Collection<E>
  4. .get(key)
  5. .remove(key)
  6. containsKey(key) and containsValue(val)
  7. keySet() -> set of keys
  8. .entrySet() -> set of Map.Entry
  9. Modifying either keySet or entrySet is reflected in the original map
  10. .forEach((key, value) -> {lambda code})
  11. replace(key, value)
  12. compute() and computeIfAbsent() and computeIfPresent() it takes in a lambda of the form (key, value) -> {} , and the return value of this lamdba is the new value, the methods IfAbsent() and IfPresent() are there to take care of calling/not calling the function if the current value is null

You can put any object as a key in a map, it'll just use it's memory address as key, note: it'll only match the key if the object requested and object in hashmap are EXACTLY same

NavigableMap Methods

  1. headMap(a) and tailMap(b) -> same rules as NavigableSet
  2. subMap(a, b)
  3. ceilingKey(), ceilingEntry() , higherKey(key) , higherEntry(), floorKey() , floorEntry(), lowerKey() and lowerEntry() all of them have similar rules as NavigableSet
  4. navigableKeySet(), descendingMap()

Regular Expressions

  1. Step 1 : Compile your regex using p = Pattern.compile(regex)
  2. Step 2: generate matcher for this pattern, p.matcher(string)
  3. use the following methods to find patterns
  4. matcher.find() returns true/false depending on whether a match was found (sorta like iterator.next())
  5. match.start() and match.end() return the starting and ending + 1 character of the matched string

Syntax

Pattern p = Pattern.compile('fhi');
Matcher m = p.matcher('fhi how are you');
m.find()/m.start()/m.end() etc etc

Regex Concepts

Character Classes

[abc] character class, : One character class matches only one character in the actual string [^abc] anything except abc [a-zA-Z]

Existing Character classes

. any character \d digit \D non digit \s any whitespace character ( tab, space, endline, crlf, etc) \S not whitespace \w word character (a-zA-Z0-9) \W not word

Quantifiers

  1. it's written in the NAME, quantifiers means they help you count how many times you want a character class, character or a capturing group repeated

* -> 0 or more + -> one or more {n} -> exactly n times {n, m} -> n to m times, m (exclusive)

Capturing Groups

() wrap these around characters that you want to group

each capturing group is given a default number based on the order of opening brackets

 In the expression ((A)(B(C))), for example, there are four such groups:

  1. ((A)(B(C)))
  2. (A)
  3. (B(C))
  4. (C)

(they are ordered in above example)

group 0 is the entire string

you can back reference groups

\d\d\d\d -> match 4 digit number (\d\d)\1 -> first 2 digit same as last 2 digit, 1 is the number of capturing group

Boundaries

  1. this too, written in the name as well
  2. It allows us to add conditions for the characters surrounding the pattern that we want to match
  3. These boundaries are only conditions that filter out matches, they are NOT included in the match

^ -> beginning of a line $ -> end of a line \b -> word boundary ( basically a non word character but it's not included in match) \B -> non word boundary ( basically a word character but not included in match)

BitManipulation

Long.bitCount() - number of 1's Long.highestOneBit() - returns (1<<i) where i is the position of the left most bit Long.lowestOneBit() returns (1<<i) where i is the position of the right most bit Long.numberOfLeadingZeros() number of zeros before the highest one bit Long.numberOfTrailingZeros() number of zeros after the lowest one bit

  1. You can use it for subsets and all (2 power something)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment