Founded: 1869 (New York City)
Founders: Marcus Goldman & Samuel Sachs
Headquarters: New York City, USA
CEO: David Solomon (since 2018)
Employees: ~45,000 (as of 2023)
Revenue (2023): $46.25 billion
Advises on M&A, IPOs, debt & equity underwriting.
Top-ranked in global M&A and IPO league tables.
Sales & trading (fixed income, equities, currencies, commodities).
Market-making and risk management services.
Manages investments for institutions and high-net-worth individuals.
Includes private equity, real estate, and alternative investments.
Consumer banking (e.g., Marcus loans & savings accounts).
Fintech partnerships (e.g., Apple Card, GreenSky).
Known for intense work culture ("work hard, play hard").
Elite recruitment (targets top universities, rigorous interviews).
Nicknames: "The Vampire Squid" (criticism for perceived financial manipulation).
2008 Financial Crisis: Received $10B in TARP funds (later repaid).
1MDB Scandal: Fined $2.9B for role in Malaysian corruption case (2020).
ESG Criticisms: Accused of hypocrisy over fossil fuel investments.
Downsizing consumer banking (sold Marcus loans, exited GreenSky).
Focus on core Wall Street services (trading, investment banking).
Goldman Sachs remains a powerhouse in finance, though its strategies evolve with market demands and regulatory pressures.
In SQL (Structured Query Language), a JOIN
clause is used to combine rows from two or more tables based on a related column between them.
Here's a breakdown:
Customers
and another for Orders
. A JOIN
allows you to retrieve data that spans these tables, such as getting a list of customers along with their corresponding order details, in a single result set.INNER JOIN
: Returns only the rows where there is a match in both tables based on the join condition. This is the most common type of join.LEFT JOIN
(or LEFT OUTER JOIN
): Returns all rows from the "left" table (the first table mentioned) and the matched rows from the "right" table (the second table mentioned). If there's no match in the right table for a row in the left table, the result will contain NULL
values for the columns from the right table.
RIGHT JOIN
(or RIGHT OUTER JOIN
): Returns all rows from the "right" table and the matched rows from the "left" table. If there's no match in the left table for a row in the right table, the result will contain NULL
values for the columns from the left table.
FULL JOIN
(or FULL OUTER JOIN
): Returns all rows when there is a match in either the left or the right table. It essentially combines the results of a LEFT JOIN
and a RIGHT JOIN
. If there's no match for a row from one table in the other, the result will contain NULL
values for the columns from the table without the match.
CROSS JOIN
: Returns the Cartesian product of the two tables, meaning it combines every row from the first table with every row from the second table. It doesn't typically use an ON
condition.
SELF JOIN
: This isn't a different type of join keyword, but rather a technique where a table is joined with itself using table aliases. This is useful for comparing rows within the same table (e.g., finding employees who report to the same manager).In essence, SQL JOIN
s are fundamental tools for querying relational databases effectively by linking related information stored across different tables.
Okay, let's break down processes and threads in the context of an Operating System (OS). Both are fundamental concepts for understanding how software executes and how operating systems manage multitasking and concurrency.
Process
new
or malloc
).
Thread
Analogy:
Think of a Process as a restaurant kitchen:
Think of Threads as chefs working within that kitchen:
Summary Table:
#include<bits/stdc++.h>
using namespace std;
//function to calculate the maximum profit attainable
int maximiseProfit(int prices[], int size)
{
int maximumProfit = 0; // stores the maximum profit
for(int i = 1; i < size; i ++)
{
if(prices[i] - prices[i - 1] > 0)
maximumProfit += prices[i] - prices[i - 1];
}
return maximiseProfit;
}
int main()
{
int prices[] = { 100, 180, 260, 310, 40, 535, 695 };
int size = sizeof(prices) / sizeof(prices[0]);
cout << maximiseProfit(prices, size) << endl;
return 0;
}
Let's break down BFS (Breadth-First Search) and DFS (Depth-First Search) — two fundamental graph traversal algorithms — with key comparisons, how they work, and when to use each.
Feature | BFS | DFS |
---|---|---|
Stands for | Breadth-First Search | Depth-First Search |
Traverses | Level by level | Deep into branches first |
Uses | Queue (FIFO) | Stack (LIFO) or recursion |
Good for | Finding shortest path (unweighted) | Exploring all paths |
Time Complexity | O(V+E)O(V + E) | O(V+E)O(V + E) |
Space Complexity | Can be high (wide graphs) | Can be high (deep graphs) |
Start at the root (or any node)
Explore all neighbors first, then their neighbors' neighbors
Ideal for shortest path in unweighted graphs
Enqueue start node
While queue isn’t empty:
Dequeue node
Visit and enqueue all unvisited neighbors
from collections import deque
def bfs(graph, start):
visited = set()
queue = deque([start])
while queue:
node = queue.popleft()
if node not in visited:
print(node)
visited.add(node)
queue.extend(graph[node] - visited)
Start at the root and go as deep as possible before backtracking
Ideal for pathfinding, detecting cycles, topological sorting, etc.
Start at node
Visit an unvisited neighbor, repeat
Backtrack when no unvisited neighbors
def dfs(graph, start, visited=None):
if visited is None:
visited = set()
visited.add(start)
print(start)
for neighbor in graph[start]:
if neighbor not in visited:
dfs(graph, neighbor, visited)
Imagine this undirected graph:
A
/ \
B C
| |
D E
BFS from A → A B C D E
(level by level)
DFS from A → A B D C E
(deep before wide — order may vary depending on implementation)
int x = 6;
// some code
int *ptr = &x;
int x = 6;
int &ref = x;
int x = 6;
int *ptr;
ptr = &x;
int y = 7;
ptr = &y;
Abstract class | Interface |
---|---|
Abstract classes can have abstract and non-abstract methods. | Interfaces are allowed to have abstract methods only. Since Java 8, interfaces can also have default and static methods. |
Abstract classes do not support multiple inheritances. | Interfaces do support multiple inheritances. |
Abstract classes can possess final, non-final, static and non-static variables. | Interfaces can only possess static and final variables. |
Abstract classes can provide the implementation of interfaces. | Interfaces cannot provide the implementation of abstract classes. |
The abstract keyword is used for declaring abstract classes. | The interface keyword is used for declaring interfaces. |
An abstract class can implement numerous Java interfaces by extending to another Java class. | An interface is allowed to extend to another Java interface only. |
Java abstract classes are allowed to have class members like private, protected, etc. | Members of a Java interface cannot be private or protected. |
The JVM is the cornerstone of Java's platform independence ("write once, run anywhere") and acts as an abstract machine that executes Java bytecode.
Here's a look at its key internal components:
1. Class Loader Subsystem
This component is responsible for dynamically loading Java classes (.class
files) into the JVM memory as they are needed by the application during runtime. It performs three main functions:
.class
file (e.g., from the classpath) and creating a corresponding Class
object representation inside the JVM. This is typically done by a hierarchy of class loaders:
java.lang.*
) from the <JAVA_HOME>/jre/lib
directory. It's implemented in native code.<JAVA_HOME>/jre/lib/ext
).-cp
or the CLASSPATH
environment variable)..class
file is structurally correct, safe, and adheres to JVM specifications. This prevents malicious code from corrupting the JVM.0
for integers, null
for objects).static {}
blocks) are executed, and static variables are assigned their actual initial values as specified in the code. This phase is triggered only when a class is actively used for the first time (e.g., an instance is created, a static method is called, or a static field is accessed).2. Runtime Data Areas (Memory Areas)
These are the memory regions allocated and managed by the JVM during program execution. They are crucial for storing data needed while the application runs.
3. Execution Engine
This is the core component responsible for executing the bytecode loaded by the Class Loader and stored in the Runtime Data Areas.
4. Native Method Interface (JNI)
5. Native Method Libraries
In Java, both StringBuffer
and StringBuilder
are used to create mutable (modifiable) sequences of characters — unlike String
, which is immutable.
Here’s a breakdown of the differences between StringBuffer
and StringBuilder
:
Feature | StringBuffer |
StringBuilder |
---|---|---|
Thread Safety | Thread-safe (synchronized) | Not thread-safe (not synchronized) |
Performance | Slower due to synchronization | Faster for single-threaded operations |
Introduced In | Java 1.0 | Java 1.5 |
Use Case | Use in multi-threaded environments | Use in single-threaded environments |
Methods | Same as StringBuilder (e.g. .append() , .insert() , .reverse() ) |
Same methods, no sync overhead |
// StringBuffer example
StringBuffer sbf = new StringBuffer("Hello");
sbf.append(" World");
System.out.println(sbf); // Output: Hello World
// StringBuilder example
StringBuilder sbl = new StringBuilder("Hello");
sbl.append(" Java");
System.out.println(sbl); // Output: Hello Java
Use **StringBuffer**
when multiple threads might access and modify the string concurrently.
Use **StringBuilder**
for better performance in single-threaded scenarios.
equals()
is a function that compares two objects to see if they are equal. It compares the Key to see if they are equivalent. It belongs to the Object class and is a method. It is possible to override it. It is required to override the hashCode()
method if you override the equals()
method.hashCode()
is an object class method. It returns the object's memory reference in integer form. The bucket number is determined by the value returned by the method. The address of the element within the map is represented by the bucket number. Null Key's hash code is 0
. Multithreading is one of Java's core strengths — let’s break it down clearly:
Multithreading is a Java feature that allows concurrent execution of two or more parts (threads) of a program to improve performance, especially on multi-core processors.
Think of a thread as a lightweight subprocess — it's the smallest unit of execution in Java.
Goal: Improve application responsiveness and resource utilization by doing multiple tasks simultaneously.
There are two main ways to create threads in Java:
Thread
classclass MyThread extends Thread {
public void run() {
System.out.println("Thread running via Thread class");
}
}
public class Test {
public static void main(String[] args) {
MyThread t1 = new MyThread();
t1.start(); // Start the thread
}
}
You override the run()
method.
Call start()
to begin execution (internally calls run()
on a new thread).
Runnable
interfaceclass MyRunnable implements Runnable {
public void run() {
System.out.println("Thread running via Runnable");
}
}
public class Test {
public static void main(String[] args) {
Thread t1 = new Thread(new MyRunnable());
t1.start(); // Start the thread
}
}
Preferred way — allows you to extend another class (Java supports single inheritance).
New: Thread is created but not started.
Runnable: After calling start()
, it's ready to run.
Running: The thread scheduler picks it up.
Blocked/Waiting: Waiting for resources or signal.
Terminated: Thread has finished execution.
Faster execution on multi-core systems
Better CPU utilization
Useful for:
Parallel processing
Background tasks (e.g., file downloads, UI updates)
Game engines, real-time applications.