在 Java 中,没有直接类似 Python 生成器的语法,但可以通过迭代器(Iterator
)和流式处理(如使用 Spliterator
或 Reactive Streams)来实现类似生成器的功能。此外,也可以通过 BlockingQueue
和线程的组合实现异步文件解压流。
以下是几种实现方式:
**Iterator**
实现一个 Iterator
,在每次调用 next()
时返回解压完成的下一个文件名称。
import java.io.File;
import java.util.Iterator;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.io.FileInputStream;
import java.io.IOException;
public class FileExtractor implements Iterator<String> {
private ZipInputStream zipInputStream;
private ZipEntry currentEntry;
public FileExtractor(String zipFilePath) throws IOException {
this.zipInputStream = new ZipInputStream(new FileInputStream(zipFilePath));
this.currentEntry = zipInputStream.getNextEntry();
}
@Override
public boolean hasNext() {
return currentEntry != null;
}
@Override
public String next() {
String fileName = currentEntry.getName();
try {
// 解压当前文件(简化为输出文件名)
System.out.println("Extracting: " + fileName);
zipInputStream.closeEntry();
currentEntry = zipInputStream.getNextEntry();
} catch (IOException e) {
throw new RuntimeException(e);
}
return fileName;
}
public void close() throws IOException {
zipInputStream.close();
}
public static void main(String[] args) throws IOException {
try (FileExtractor extractor = new FileExtractor("example.zip")) {
while (extractor.hasNext()) {
System.out.println("Extracted file: " + extractor.next());
}
}
}
}
**BlockingQueue**
+ 生产者-消费者模型使用 BlockingQueue
来将文件名逐个传递给调用者,调用者阻塞等待每个文件名的返回。
import java.io.FileInputStream;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
public class FileExtractorProducerConsumer {
private final BlockingQueue<String> queue = new LinkedBlockingQueue<>();
private final Thread producerThread;
public FileExtractorProducerConsumer(String zipFilePath) {
producerThread = new Thread(() -> {
try (ZipInputStream zipInputStream = new ZipInputStream(new FileInputStream(zipFilePath))) {
ZipEntry entry;
while ((entry = zipInputStream.getNextEntry()) != null) {
queue.put(entry.getName());
System.out.println("Extracting: " + entry.getName());
zipInputStream.closeEntry();
}
queue.put("EOF"); // Signal end of processing
} catch (Exception e) {
throw new RuntimeException(e);
}
});
producerThread.start();
}
public String nextFile() throws InterruptedException {
String fileName = queue.take();
if ("EOF".equals(fileName)) {
return null;
}
return fileName;
}
public void close() throws InterruptedException {
producerThread.join();
}
public static void main(String[] args) throws Exception {
FileExtractorProducerConsumer extractor = new FileExtractorProducerConsumer("example.zip");
String fileName;
while ((fileName = extractor.nextFile()) != null) {
System.out.println("Processed file: " + fileName);
}
extractor.close();
}
}
**Stream**
和 ****Spliterator**
通过 Stream
和 Spliterator
提供懒加载的解压文件流。
import java.io.FileInputStream;
import java.util.Spliterator;
import java.util.function.Consumer;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
public class FileExtractorStream implements Spliterator<String> {
private final ZipInputStream zipInputStream;
private ZipEntry currentEntry;
public FileExtractorStream(String zipFilePath) throws Exception {
this.zipInputStream = new ZipInputStream(new FileInputStream(zipFilePath));
this.currentEntry = zipInputStream.getNextEntry();
}
@Override
public boolean tryAdvance(Consumer<? super String> action) {
if (currentEntry == null) {
return false;
}
action.accept(currentEntry.getName());
try {
zipInputStream.closeEntry();
currentEntry = zipInputStream.getNextEntry();
} catch (Exception e) {
throw new RuntimeException(e);
}
return currentEntry != null;
}
@Override
public Spliterator<String> trySplit() {
return null; // Single-threaded processing
}
@Override
public long estimateSize() {
return Long.MAX_VALUE;
}
@Override
public int characteristics() {
return NONNULL | ORDERED;
}
public void close() throws Exception {
zipInputStream.close();
}
public static void main(String[] args) throws Exception {
try (FileExtractorStream extractor = new FileExtractorStream("example.zip")) {
extractor.stream().forEach(fileName -> System.out.println("Extracted: " + fileName));
}
}
public java.util.stream.Stream<String> stream() {
return java.util.stream.StreamSupport.stream(this, false);
}
}
如果需要异步支持,可以结合 CompletableFuture
实现。
import java.io.*;
import java.util.concurrent.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
public class AsyncFileExtractor {
private final BlockingQueue<CompletableFuture<String>> queue = new LinkedBlockingQueue<>();
private final ExecutorService executor = Executors.newSingleThreadExecutor();
public AsyncFileExtractor(String zipFilePath, String outputDir) {
executor.submit(() -> {
try (ZipInputStream zipInputStream = new ZipInputStream(new FileInputStream(zipFilePath))) {
ZipEntry entry;
byte[] buffer = new byte[1024];
while ((entry = zipInputStream.getNextEntry()) != null) {
CompletableFuture<String> future = new CompletableFuture<>();
queue.put(future);
File outputFile = new File(outputDir, entry.getName());
outputFile.getParentFile().mkdirs();
try (FileOutputStream fos = new FileOutputStream(outputFile)) {
int len;
while ((len = zipInputStream.read(buffer)) > 0) {
fos.write(buffer, 0, len);
}
}
System.out.println("Extracted and written: " + outputFile.getAbsolutePath());
future.complete(outputFile.getAbsolutePath());
zipInputStream.closeEntry();
}
queue.put(CompletableFuture.completedFuture(null)); // EOF
} catch (Exception e) {
queue.put(CompletableFuture.failedFuture(e));
}
});
}
public CompletableFuture<String> nextFile() throws InterruptedException {
return queue.take();
}
public void close() {
executor.shutdown();
}
public static void main(String[] args) throws Exception {
String zipFilePath = "example.zip";
String outputDir = "output";
AsyncFileExtractor extractor = new AsyncFileExtractor(zipFilePath, outputDir);
CompletableFuture<String> future;
while ((future = extractor.nextFile()).get() != null) {
System.out.println("Processed file: " + future.get());
}
extractor.close();
}
}
**Iterator**
: 简单直接,适合单线程拉取。**BlockingQueue**
: 适合生产者-消费者模式,支持多线程并发处理。**Stream**
** 和 ****Spliterator**
: 流式懒加载,语法更简洁,更现代。选择实现方式取决于你是否需要多线程支持,以及调用方的消费方式。