第 9 章 · 标准库对照速查¶
资深 Java 工程师上手干活最大的空白往往是:"我熟悉 java.util / java.time / java.nio.file / java.util.regex,Python 里对应啥?" 本章是一张高频标准库地图,每节给出 Java 对应 + Pythonic 用法。不深入,只给"日常能干活"的对照。
9.1 collections ↔ java.util¶
| Python | Java 对应 | 用途 |
|---|---|---|
Counter |
(手动 Map<K,Integer>) |
计数 |
defaultdict |
computeIfAbsent |
缺 key 自动建默认值 |
deque |
ArrayDeque |
双端队列 |
namedtuple |
record(轻量) |
位置 + 命名访问的不可变元组 |
OrderedDict |
LinkedHashMap |
保序字典(3.7+ 普通 dict 已保序,多数场景不再需要) |
from collections import Counter, defaultdict, deque
Counter("abracadabra").most_common(2) # [('a', 5), ('b', 2)]
dd = defaultdict(list)
dd["k"].append(1) # 不用先判 key
dq = deque([1, 2, 3])
dq.appendleft(0); dq.popleft() # O(1) 头部操作
9.2 itertools ↔ Stream API¶
Python 的 itertools 是 Stream 的"前辈",提供惰性迭代工具:
| Python | Java 对应 | 作用 |
|---|---|---|
chain(a, b) |
Stream.concat |
拼接 |
islice(it, n) |
stream.limit(n) |
取前 n 个 |
groupby(it, key) |
Collectors.groupingBy |
分组(注意:需先排序,按连续 key 分组) |
product(A, B) |
嵌套循环 / 笛卡尔积 | 笛卡尔积 |
combinations/pemutations |
(手写) | 组合 / 排列 |
accumulate |
stream reduce 前缀 |
前缀和等 |
from itertools import chain, groupby, product
list(chain([1, 2], [3, 4])) # [1,2,3,4]
list(product("AB", "12")) # [('A','1'),('A','2'),('B','1'),('B','2')]
# groupby 必须先按 key 排序,它只分"连续相同"的段
data = sorted([("a",1),("b",2),("a",3)], key=lambda x: x[0])
for k, group in groupby(data, key=lambda x: x[0]):
print(k, list(group)) # a [('a',1),('a',3)] ; b [('b',2)]
⚠️ Java 程序员的陷阱
itertools.groupby 和 Java 的 groupingBy 行为不同:它只把相邻且 key 相同的元素分组,所以必须先按 key 排序。想完全等同 groupingBy,更省心的做法是用 defaultdict(list) 手动收集。
9.3 pathlib ↔ java.nio.file¶
Python 处理路径的现代方式是 pathlib(比老的 os.path 拼字符串优雅得多):
| Python | Java |
|---|---|
Path("a") / "b.txt" |
Path.of("a","b.txt") |
p.read_text(encoding="utf-8") |
Files.readString(p) |
p.write_text(s) |
Files.writeString(p, s) |
p.exists() / p.is_file() |
Files.exists / isRegularFile |
p.parent / p.name / p.suffix |
p.getParent / getFileName |
Path(".").glob("*.py") |
Files.list + 过滤 |
from pathlib import Path
docs = Path("docs")
for md in docs.rglob("*.md"): # 递归 glob
print(md, md.stat().st_size)
(docs / "notes.txt").write_text("hi", encoding="utf-8")
Pythonic 写法
拼路径用 / 运算符(Path("a") / "b"),永远不要用字符串 + 拼(跨平台分隔符问题)。
9.4 datetime ↔ java.time¶
| Python | Java |
|---|---|
date(2026, 6, 16) |
LocalDate |
datetime.now() |
LocalDateTime.now() |
datetime.now(timezone.utc) |
Instant.now() |
timedelta(days=1) |
Duration / Period |
dt.strftime("%Y-%m-%d") |
DateTimeFormatter |
datetime.fromisoformat(s) |
parse |
from datetime import datetime, date, timedelta, timezone
now = datetime.now() # naive(无时区,慎用)
utc = datetime.now(timezone.utc) # aware(带时区,推荐)
tomorrow = date.today() + timedelta(days=1)
formatted = now.strftime("%Y-%m-%d %H:%M")
parsed = datetime.fromisoformat("2026-06-16T10:00:00")
⚠️ Java 程序员的陷阱
Python 的 datetime.now() 默认是 naive(无时区信息),存/传时容易踩夏令时坑。推荐带时区:datetime.now(timezone.utc),或用第三方 zoneinfo(标准库,3.9+)指定时区。
9.5 re ↔ java.util.regex¶
import re
re.findall(r"\d+", "a1b22c333") # ['1', '22', '333']
re.sub(r"\s+", "-", "a b c") # 'a-b-c'
m = re.search(r"(\w+)@(\w+)", "mail: x@y.com")
m.group(1), m.group(2) # ('x', 'y')
# 预编译(循环里用,提升性能)
pattern = re.compile(r"\d{4}")
pattern.findall("2026 and 1999")
| Python | Java |
|---|---|
re.match |
Pattern.matcher + matches(从头匹配) |
re.search |
find(任意位置) |
re.findall |
循环 find + group |
re.sub |
replaceAll |
r"raw string" |
Pattern.compile |
Pythonic 写法
正则字符串前加 r(raw string),避免反斜杠转义地狱:r"\d+\s" 而不是 "\\d+\\s"。
9.6 json ↔ Jackson / Gson¶
import json
data = {"name": "小明", "age": 30}
text = json.dumps(data, ensure_ascii=False, indent=2) # 序列化
obj = json.loads(text) # 反序列化
| Python | Java |
|---|---|
json.dumps(obj) |
objectMapper.writeValueAsString |
json.loads(s) |
objectMapper.readValue |
ensure_ascii=False |
(默认就是 UTF-8) |
要点
- 默认
dumps会把非 ASCII 转义:"小明"→"小明"。中文项目务必ensure_ascii=False。 - 复杂对象模型(嵌套、校验)用第三方
pydantic(FastAPI 底层),相当于 Jackson + Bean Validation。
9.7 csv ↔ Apache Commons CSV¶
import csv
with open("data.csv", newline="", encoding="utf-8") as f:
reader = csv.DictReader(f) # 每行是 dict
for row in reader:
print(row["name"], row["age"])
9.8 subprocess ↔ ProcessBuilder¶
import subprocess
result = subprocess.run(
["git", "status"],
capture_output=True, text=True, check=True
)
print(result.stdout)
# check=True 时非零退出码抛 CalledProcessError
⚠️ Java 程序员的陷阱
别再用老式的 os.system() 或 subprocess.run(..., shell=True) 拼 shell 字符串——有命令注入风险。用列表形式 ["git", "status", path] 传参,让系统正确转义。
9.9 logging ↔ slf4j / log4j¶
import logging
logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s %(message)s")
log = logging.getLogger(__name__) # 按模块命名 logger
log.info("user %s logged in", user_id) # 用 %s 占位,延迟格式化(别用 f-string)
| Python | Java |
|---|---|
logging.getLogger(__name__) |
LoggerFactory.getLogger(Class) |
log.info / warning / error |
log.info / warn / error |
basicConfig |
logback.xml 配置 |
Pythonic 写法
日志消息用 log.info("user %s done", uid) 而非 log.info(f"user {uid} done")——前者只在真的输出时才格式化,且级别不够时不做无用拼接。
9.10 其他高频工具速查¶
| 领域 | Python | Java |
|---|---|---|
| 数学 | math, statistics, random |
Math, Random |
| 函数工具 | functools(lru_cache, partial, reduce), operator |
Function, Collectors.reducing |
| 操作系统 | os, sys, shutil |
System, Files |
| 临时文件 | tempfile |
Files.createTempFile |
| 类型/反射 | typing, inspect, dataclasses |
java.lang.reflect |
| 并发(第 11 章) | threading, multiprocessing, concurrent.futures, asyncio |
java.util.concurrent |
本章练习¶
练习 9.1
用 pathlib 写:统计 docs/ 目录下所有 .md 文件的总行数。
参考答案
练习 9.2
用 Counter 统计一段英文里每个单词的出现次数(忽略大小写、去标点),取前 3。
参考答案
练习 9.3
用 subprocess 安全地运行 ping 某主机 3 次,捕获输出;非零退出时打印错误。
参考答案
上一章:第 8 章 · 类型提示 | 下一章:第 10 章 · 工程化基础。