-
Which release introduced the Collections Framework, and what was there before it?
- J2SE 1.2, in 1998. Before that there were only
Vector,Hashtable,StackandEnumeration. They had no common interface, so you could not write a method that accepted "any collection", and they synchronised every method whether you needed it or not.
- J2SE 1.2, in 1998. Before that there were only
-
Why is
Mapnot aCollection?- A
Collectionholds single elements; aMapholds pairs. Making one extend the other would have forced both into an awkward shape. InsteadMapoffers three views onto itself:keySet(),values()andentrySet().
- A
-
When should you use
ArrayListand whenLinkedList?- Use
ArrayListalmost always. It stores elements in one array, so reading by index is instant; inserting in the middle has to shift the rest.LinkedListis the opposite: inserting at a position you already hold is instant, but reading by index has to walk the chain. Its most useful role is as aDeque.
- Use
-
What is the difference between
HashMap,LinkedHashMapandTreeMap?HashMapis the fastest and keeps no order.LinkedHashMapcosts the same and remembers the order you inserted keys in.TreeMapkeeps the keys sorted, and pays a small extra cost per operation for that.
-
What is the difference between
ComparableandComparator?Comparableis the class's own idea of order. You writecompareToinside the class and you get exactly one ordering.Comparatoris defined outside the class, so you can write as many different orderings as you like without touching the class itself.
-
What does the diamond operator do?
- It saves you from repeating the type arguments.
new HashMap<>()instead ofnew HashMap<String, List<Integer>>(). The compiler works them out from the left-hand side. Added in Java 7.
- It saves you from repeating the type arguments.
-
What is try-with-resources?
- A
tryblock that closes its resources for you. Anything declared in thetry (...)header that implementsAutoCloseableis closed at the end of the block, whether the block finished normally, returned, or threw. Several resources are closed in reverse order.
- A
-
What is multi-catch?
- Java 7 syntax for handling several unrelated exception types in one block:
catch (IOException | SQLException ex). It saves duplicating the same handling code twice. The variable is implicitly final.
- Java 7 syntax for handling several unrelated exception types in one block:
-
What is autoboxing?
- The compiler converting between primitives and their wrapper types for you, so you can write
list.add(5)on aList<Integer>. Added in Java 5.
- The compiler converting between primitives and their wrapper types for you, so you can write
-
Why is an
enumbetter than a group ofintconstants?- It is type-safe, so only the enum's own constants can be passed. It prints a name instead of a number. It can carry fields and methods. And it works properly with
switch,EnumMap,EnumSetand serialization.
- It is type-safe, so only the enum's own constants can be passed. It prints a name instead of a number. It can carry fields and methods. And it works properly with
-
What are varargs?
- A parameter that accepts any number of arguments:
int sum(int... numbers). Inside the method it is a normal array. A method can have only one, and it must be the last parameter.
- A parameter that accepts any number of arguments:
-
What is a static import?
import static java.lang.Math.max;lets you writemax(a, b)instead ofMath.max(a, b). It is handy forMathand for constants, and confusing if you overuse it, because the reader cannot see where the name came from.
-
StringBuilderorStringBuffer?StringBuilder, unless the object is genuinely shared between threads. They have the same API, butStringBuffersynchronises every method, which you almost never need.
-
What is the difference between
Thread.start()andThread.run()?start()asks the JVM for a new thread, which then callsrun(). Callingrun()yourself just runs the method on the current thread — no new thread is created at all.
-
What does
Arrays.asListreturn?- A fixed-size view over the array you passed in. You can replace elements with
set, butaddandremovethrowUnsupportedOperationException. It is not the same as Java 9'sList.of, which is fully immutable.
- A fixed-size view over the array you passed in. You can replace elements with
-
What is type erasure, and name three things it makes impossible.
- The compiler checks your type arguments and then removes them, so at run time
List<String>andList<Integer>are both justList. Because of that: you cannot writenew T()ornew T[10]; you cannot writex instanceof List<String>; and you cannot have two overloadsf(List<String>)andf(List<Integer>), because both erase to the same signature.
- The compiler checks your type arguments and then removes them, so at run time
-
Why was erasure chosen instead of keeping the types at run time?
- Compatibility. Code compiled before Java 5 had to keep running on the new JVM, and new generic code had to work with old libraries. Erasing the type arguments meant the class-file format and the JVM did not have to change at all.
-
What is a bridge method?
- A hidden method the compiler generates so that overriding still works after erasure. If
Child implements Comparable<Child>withcompareTo(Child), the compiler adds acompareTo(Object)that casts its argument and calls yours, becauseObjectis the erased parameter type the interface really declares.
- A hidden method the compiler generates so that overriding still works after erasure. If
-
Explain PECS.
- Producer
extends, Consumersuper. If a parameter only gives you values, declare it? extends T— you can read, not add. If it only receives values from you, declare it? super T— you can add, and reads only give youObject.Collections.copy(List<? super T> dest, List<? extends T> src)uses both in one signature.
- Producer
-
Why is
List<Integer>not aList<Number>, when arrays allow the equivalent?- If it were allowed, you could assign a
List<Integer>to aList<Number>variable and then add aDoubleto it, which would corrupt the original list. Arrays do allow this (Object[] a = new Integer[3]), and the mistake shows up only at run time as anArrayStoreException. Generics were designed to catch the same mistake at compile time.
- If it were allowed, you could assign a
-
Why does
Integer a = 127, b = 127; a == bprinttrue, but the same with 128 printsfalse?Integer.valueOfkeeps a cache of the values from −128 to 127, so both variables point at the same cached object. Above 127 each boxing creates a new object, and==compares references, not values. Useequalsfor boxed types.
-
What are the four kinds of nested class?
- Static nested, which has no link to an outer object. Inner (non-static), which holds a hidden reference to the outer object. Local, declared inside a method body. Anonymous, declared and created in a single expression. All except static nested keep the enclosing object alive, which can be a memory leak.
-
What is a suppressed exception?
- When a try-with-resources block throws and
close()then also throws, the block's exception is the one that propagates, andclose()'s exception is attached to it. You read it back withgetSuppressed(). The oldtry/finallypattern lost the original exception instead, which made debugging much harder.
- When a try-with-resources block throws and
-
What is precise rethrow?
- Java 7 taught the compiler to look at which exceptions the
tryblock can actually throw, rather than only at the declared type of the catch parameter. Socatch (Exception ex) { throw ex; }can sit inside a method declaredthrows IOException, as long asexis never reassigned.
- Java 7 taught the compiler to look at which exceptions the
-
How is a
switchon aStringcompiled?- Into a
switchonhashCode()to find a candidate label, followed by anequals()call to confirm it. So it stays fast even with many cases. PassingnullthrowsNullPointerException;case nullonly became legal with Java 21's pattern matching.
- Into a
-
What does NIO.2 give you over
java.io.File?- Real error messages.
Filemethods returnedfalseon failure without saying why;Filesthrows a specific exception such asNoSuchFileExceptionthat names the file. On top of that you get symbolic-link support, file attributes, atomic moves,walkFileTreeandWatchService.
- Real error messages.
-
Why use an
ExecutorServiceinstead of creating threads yourself?- It separates the task from the thread that runs it. You get thread reuse, a fixed upper limit on how many run at once, a queue for the rest, a
Futureto collect the result, and an orderly shutdown. Creating one thread per task gives you none of that.
- It separates the task from the thread that runs it. You get thread reuse, a fixed upper limit on how many run at once, a queue for the rest, a
-
What is the difference between
RunnableandCallable?Runnable.run()returns nothing and cannot throw a checked exception.Callable<V>.call()returns a value and may throw. Submitting aCallableto an executor gives you aFuture<V>, and if the task throws,get()reports it as anExecutionExceptionwrapping the original.
-
What does
volatileguarantee, and what does it not?- It guarantees that a write is visible to other threads straight away, and that reads and writes are not reordered around it. It does not make anything atomic.
volatile int i; i++;is still three steps and still loses updates. Use it for a stop flag; useAtomicIntegeror a lock for a counter.
- It guarantees that a write is visible to other threads straight away, and that reads and writes are not reordered around it. It does not make anything atomic.
-
Why must
wait()always be called inside a loop?- Because a thread can wake up without anyone notifying it — the specification allows these "spurious wakeups". And with
notifyAll, several threads wake up for a single item, so all but one will find nothing there. In both cases you have to re-check the condition after waking, which meanswhile, notif.
- Because a thread can wake up without anyone notifying it — the specification allows these "spurious wakeups". And with
-
How does
ConcurrentHashMapdiffer fromCollections.synchronizedMap?synchronizedMapputs one lock around the whole map and its iterators still throwConcurrentModificationException.ConcurrentHashMaplocks only the part of the map being touched, offers atomic compound operations likeputIfAbsentandcompute, and its iterators never throw — they just may or may not show writes made after iteration started.
-
What is fork/join, and where is it used today?
- A pool designed for splitting one large task into smaller ones. Each worker has its own queue and idle workers steal tasks from busy ones. You write a
RecursiveTaskthat splits until the piece is small, then computes. Java 8's parallel streams run on the sharedForkJoinPool.commonPool(), which is why they suit CPU-bound work over easily-split sources.
- A pool designed for splitting one large task into smaller ones. Each worker has its own queue and idle workers steal tasks from busy ones. You write a
-
List the real differences between a lambda and an anonymous class implementing the same interface.
thisis the anonymous object inside an anonymous class, but the enclosing object inside a lambda, because a lambda does not create a new scope for it. An anonymous class compiles to a real class file that is loaded like any other; a lambda compiles to aninvokedynamiccall site thatLambdaMetafactorylinks on first use, and a lambda that captures nothing can be reused as a single instance instead of allocated each time. An anonymous class can extend a class, hold fields and implement several methods; a lambda can only target one functional interface. Anonymous classes also introduce a new scope, so they can shadow names that a lambda cannot.
-
Why did captured variables have to be
finalbefore Java 8, and what changed?- The captured value is copied into a field of the generated class. If the original local could still change, the copy would go stale, and nobody reading the code could tell which value they were looking at. Java forbids the situation rather than defining confusing behaviour. Java 8 kept the rule and relaxed only the syntax: if the compiler can prove you never reassign the variable, you no longer have to write
final. The old workaround for a value that genuinely had to change was a one-elementfinalarray.
- The captured value is copied into a field of the generated class. If the original local could still change, the copy would go stale, and nobody reading the code could tell which value they were looking at. Java forbids the situation rather than defining confusing behaviour. Java 8 kept the rule and relaxed only the syntax: if the compiler can prove you never reassign the variable, you no longer have to write
-
State the
equals/hashCodecontract and describe what breaks when it is violated.- Equal objects must return equal hash codes, and the hash code must not change while the object is in a hash-based collection. Override
hashCodebut notequals, and every object stays unique, so lookups miss. Overrideequalsbut nothashCode, and two equal objects can land in different buckets — aHashSetwill hold both, andmap.get(key)returnsnullfor a key that is equal to a stored one. Change a field used byhashCodeafter inserting the object and you lose the entry completely: it is in the old bucket, and every lookup goes to the new one.
- Equal objects must return equal hash codes, and the hash code must not change while the object is in a hash-based collection. Override
-
How does
HashMaphandle collisions, and what changed in Java 8?- Keys that land in the same bucket are kept in a list. Before Java 8 that list was always a linked list, so in the worst case — many keys with the same hash — lookups degraded to O(n). Java 8 converts a bucket to a red-black tree once it holds 8 entries and the table is at least 64 long, which makes the worst case O(log n). This was a security fix as much as a performance one: attackers could deliberately send colliding keys to slow a server to a halt.
HashMapalso spreads the hash withh ^ (h >>> 16)so the high bits influence which bucket is chosen.
- Keys that land in the same bucket are kept in a list. Before Java 8 that list was always a linked list, so in the worst case — many keys with the same hash — lookups degraded to O(n). Java 8 converts a bucket to a red-black tree once it holds 8 entries and the table is at least 64 long, which makes the worst case O(log n). This was a security fix as much as a performance one: attackers could deliberately send colliding keys to slow a server to a halt.
-
Why is static state shared between
Box<String>andBox<Integer>?- Because after erasure there is only one
Boxclass. The type arguments exist in the source and in the compiler's checks, not in the loaded class, so there is exactly one copy of each static field. The same reasoning explains why a static method cannot mention the class's type parameter: a type argument belongs to an instance's type, and a static method has no instance.
- Because after erasure there is only one
-
What is heap pollution, and what does
@SafeVarargsactually promise?- A
T...parameter is really aT[]. Arrays know their element type at run time, generics do not, so aList<String>[]can be aliased throughObject[]and have aList<Integer>stored into it. Nothing fails at that moment; theClassCastExceptionappears later, somewhere unrelated. That is heap pollution.@SafeVarargschecks nothing — it is the author asserting that the method never writes into the varargs array and never lets it escape. It is restricted tostatic,finalandprivatemethods, because an overriding method could break the promise.
- A
-
What does the Java Memory Model (Java 5, JSR 133) actually guarantee?
- It defines a happens-before relation, and anything not connected by it may simply never become visible to another thread, because the compiler and the CPU are free to reorder. The main edges are: releasing a monitor happens-before any later acquisition of the same monitor; a
volatilewrite happens-before every later read of that field;Thread.start()happens-before everything the new thread does; everything a thread does happens-before another thread'sjoin()returns; and afinalfield assigned in a constructor is visible to any thread afterwards, providedthisdid not escape during construction.
- It defines a happens-before relation, and anything not connected by it may simply never become visible to another thread, because the compiler and the CPU are free to reorder. The main edges are: releasing a monitor happens-before any later acquisition of the same monitor; a
-
Why is double-checked locking broken without
volatile?instance = new Singleton()is three operations: allocate memory, run the constructor, assign the reference. The JVM is allowed to reorder the last two. Another thread can then see a non-null reference to an object whose constructor has not finished, and read uninitialised fields. Declaring the fieldvolatileforbids that reordering — but only from Java 5, when the memory model was rewritten; before that the pattern was unfixable. In practice a static holder class or an enum singleton is simpler and correct without any of this.
-
When does
ThreadLocalleak memory?- The values live in a map owned by the
Threadobject. In a thread pool the thread outlives the request, so anything you never remove stays reachable for as long as the pool does. The map holds its keys weakly but its values strongly, so even a discardedThreadLocalcan keep its value pinned until that thread is reused. Always callremove()in afinallyblock. Java 21'sScopedValueexists partly to remove this whole category of bug.
- The values live in a map owned by the
-
Explain fail-fast, fail-safe and weakly consistent iterators.
- Fail-fast (
ArrayList,HashMap): the collection counts its structural changes and the iterator checks that count on every step, throwingConcurrentModificationExceptionwhen it changes. It is best effort — a detection aid, not a guarantee. Fail-safe or snapshot (CopyOnWriteArrayList): the iterator walks a frozen copy, so it never throws and never sees later writes. Weakly consistent (ConcurrentHashMap): the iterator walks the live structure, never throws, and may or may not reflect writes made after it started.
- Fail-fast (
-
Why should
compareTonever be written asa - b?- Because integer subtraction overflows. With
a = Integer.MIN_VALUEandb = 1, the difference wraps around to a positive number, so the comparator claims the smallest possible value is the larger one. That breaksTreeMap's invariants silently, and since Java 7 replaced the sort algorithm with TimSort it can also throwIllegalArgumentException: Comparison method violates its general contract!from an apparently unrelated sort. UseInteger.compare(a, b).
- Because integer subtraction overflows. With
-
What is
invokedynamicand why does the Java language itself depend on it now?- It is a call instruction whose target is not fixed in the class file. Instead it names a bootstrap method that runs once, the first time the line executes, and returns a
MethodHandlefor the JVM to link. After that the call is optimised like a static one. It was added in Java 7 for dynamic languages on the JVM, and then Java itself became its main user: lambdas (LambdaMetafactory, Java 8), string concatenation (StringConcatFactory, Java 9) andswitchpattern matching (SwitchBootstraps, Java 21). The advantage is that these features need no new bytecode, so how they work can be improved without recompiling anyone's code.
- It is a call instruction whose target is not fixed in the class file. Instead it names a bootstrap method that runs once, the first time the line executes, and returns a