Python for Java Developers: A Practical Migration Guide
TL;DR: Your Java knowledge maps almost one-to-one:
ArrayList→list,HashMap→dict, streams → comprehensions,try/catch→try/except, methods → plain functions (no class required). What you must unlearn: type declarations, getters/setters, and semicolons. What's genuinely new: slicing, comprehensions, truthiness, and*args. Every example below runs as-is in Cubemate — where you can also run the Java version next to it.
Java teaches discipline: everything in a class, every type declared, every exception accounted for. Python removes most of that ceremony — which feels liberating for about an hour, and then disorienting, because the structure you navigate by is gone. This guide is the map: each section takes something you do in Java and shows what it becomes.
Where did public static void main go?
A Python file just executes, top to bottom. No class wrapper, no entry point, no compilation step:
x = 5
price = 19.99
name = "Cubemate"
active = True
print(f"{name}: {x} items at {price}, active={active}")
No declarations — x = 5 creates an int because 5 is an int. Variables are names bound to objects, and the object has the type. (Python does support optional type hints — x: int = 5 — which large codebases use with a checker; they'll feel familiar and comforting, but nothing enforces them at runtime.)
The f"..." string is Python's String.format: expressions go directly inside {}.
Collections: ArrayList → list, HashMap → dict
The two workhorses, with their Java habits translated:
# ArrayList<Integer> nums = new ArrayList<>(); →
nums = [10, 20, 30]
nums.append(40) # .add()
first = nums[0] # .get(0)
nums.remove(20) # .remove(Integer.valueOf(20)) — by value
size = len(nums) # .size() — a function, not a method
# HashMap<String, Integer> ages = new HashMap<>(); →
ages = {"asha": 21, "rahul": 23}
ages["meera"] = 22 # .put()
age = ages["asha"] # .get() — KeyError if missing
exists = "rahul" in ages # .containsKey()
safe = ages.get("kiran", 0) # .getOrDefault()
for name, age in ages.items(): # entrySet() loop
print(name, age)
No generics, no boxing, mixed types allowed. The in operator and len() function work uniformly across lists, dicts, strings, and sets — one of Python's quiet consistencies.
Two things Java doesn't give you:
letters = ["a", "b", "c", "d", "e"]
print(letters[1:4]) # ['b', 'c', 'd'] — subList, as syntax
print(letters[-1]) # 'e' — last element, no size()-1 dance
print(letters[::-1]) # reversed copy
Slicing alone eliminates a whole category of index arithmetic you write in Java.
Loops: the for (int i = 0; ...) reflex
Python's for is Java's for-each — there is no three-part for at all:
languages = ["Python", "Java", "Go"]
for lang in languages: # for (String lang : languages)
print(lang)
for i, lang in enumerate(languages): # when you truly need the index
print(i, lang)
for i in range(5): # for (int i = 0; i < 5; i++)
print(i)
The switcher's tell is writing for i in range(len(languages)) and indexing inside the loop — it works, but it reads as Java-with-a-accent. Iterate over the items directly; reach for enumerate only when the index itself matters.
Streams → comprehensions
The transformation pipeline you build with stream().filter().map().collect() is a single expression:
numbers = [1, 2, 3, 4, 5, 6, 7, 8]
squares_of_evens = [n * n for n in numbers if n % 2 == 0]
print(squares_of_evens) # [4, 16, 36, 64]
# Also works for dicts and sets:
name_lengths = {name: len(name) for name in ["asha", "rahul"]}
Read it left to right: collect n * n, for each n in numbers, if n is even. This is the single most important idiom to adopt — resisting it is the clearest sign of writing Java in Python syntax.
Exceptions: try/except, and nothing is checked
try:
result = 10 / 0
except ZeroDivisionError as e:
print(f"caught: {e}")
finally:
print("runs either way")
The structural difference from Java: no checked exceptions. Nothing forces you to catch or declare anything; the culture is to catch what you can meaningfully handle and let the rest propagate. finally behaves as you expect, and there's an else clause that runs only when no exception occurred.
Classes without the boilerplate
class Student:
def __init__(self, name, credits=0): # constructor
self.name = name # no field declarations —
self.credits = credits # assignment creates them
def award(self, amount):
self.credits += amount
return self.credits
s = Student("Asha")
s.award(50)
print(s.name, s.credits) # direct attribute access — no getters
The adjustments, in order of discomfort:
selfis explicit — it's Java'sthis, but you write it as the first parameter of every method.- No getters/setters. Public attributes are idiomatic. (If you later need logic behind an attribute,
@propertyadds it without changing the call site — which is exactly the future-proofing getters exist for in Java.) - No
private. A single leading underscore (self._balance) is a convention meaning "internal" — respected by tooling and humans, enforced by nobody. - Default parameter values (
credits=0) replace most constructor overloading; there is no method overloading.
Small things that trip Java developers daily
| Java habit | Python reality |
|---|---|
&&, ` |
|
switch |
match statement (3.10+), or if/elif chains |
null |
None — test with is None, not == |
"a" + x string concat |
works only between strings — use f-strings |
| Empty collections are just empty | empty list/dict/string are falsy: if items: means "if non-empty" |
| Semicolons and braces | indentation is the block structure — 4 spaces, and it's enforced |
The fastest way through this list
Reading a mapping guide gets you oriented; running the pairs makes it permanent. Every snippet above executes in Cubemate's sandbox — and so does its Java equivalent, in the same chat, which turns "I think that's how Python does it" into a ten-second experiment. For any section that deserves a full chapter (comprehensions and OOP usually do), ask Bookmate for a tutorial like "idiomatic Python collections for a Java developer" and you'll get a book-style walkthrough with runnable examples.
For the general method behind this guide — and the two-week plan for any language switch — see How to Learn a Second Programming Language.
Frequently asked questions
Is Python easy to learn for Java developers?
Yes — usually one to two weeks to basic productivity. Everything conceptual (OOP, collections, exceptions, typing discipline) transfers directly; the work is learning Python's lighter syntax and its idioms like comprehensions, slicing, and duck typing that have no direct Java equivalent.
What is the Python equivalent of ArrayList and HashMap?
Java's ArrayList maps to Python's built-in list, and HashMap maps to the built-in dict. Both are untyped by default, support literal syntax ([1, 2, 3] and {'a': 1}), and are used constantly — no imports, no generics, no boxing.
Does Python have a main method like Java?
No required entry point — a Python file executes top to bottom. The conventional equivalent is the 'if __name__ == "__main__":' guard, which runs a block only when the file is executed directly rather than imported.
What replaces Java streams in Python?
List comprehensions and generator expressions. Java's stream().filter().map().collect() chain is typically one readable line in Python: [n * n for n in numbers if n % 2 == 0].
Where can I practice Python without installing it?
Cubemate runs both Java and Python in the same browser-based sandbox, which makes it well suited to switching — you can run the Java version and the Python version of the same code side by side with zero local setup.
Ready to start coding?
Write, run, and learn code in your browser — Python, Java, Go, Rust and more. No installation, no setup. Free 7-day trial.
Start Free