java竞赛优化输入输出效率

在编程竞赛中,输入输出效率至关重要。Java 的 `Scanner` 和 `System.out.println` 虽然简单,但在处理大规模数据时会严重拖慢速度。以下是 **竞赛专用输入输出模板** 及其原理详解,助你轻松应对高频 I/O 场景。

---### ⚡ 竞赛级输入输出模板(Java)
 

import java.io.*;
import java.util.*;

public class Main {
    public static void main(String[] args) throws IOException {
        // 初始化输入输出
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
        StringTokenizer st;
        
        // 读取第一行数据(示例:N 和 Q)
        st = new StringTokenizer(br.readLine());
        int N = Integer.parseInt(st.nextToken());
        int Q = Integer.parseInt(st.nextToken());
        
        // 读取第二行数据(示例:N个整数)
        int[] arr = new int[N];
        st = new StringTokenizer(br.readLine());
        for (int i = 0; i < N; i++) {
            arr[i] = Integer.parseInt(st.nextToken());
        }
        
        // 处理 Q 次操作
        while (Q-- > 0) {
            st = new StringTokenizer(br.readLine());
            String op = st.nextToken();
            if (op.equals("1")) {
                int x = Integer.parseInt(st.nextToken());
                int y = Integer.parseInt(st.nextToken());
                // 处理操作1...
            } else {
                int x = Integer.parseInt(st.nextToken());
                // 处理操作2...
                bw.write(result + "\n"); // 使用 BufferedWriter 输出
            }
        }
        
        // 最终刷新输出
        bw.flush();
    }
}


```

---

### 为什么这样写更快?


1. **BufferedReader vs Scanner**:
   - `Scanner` 会进行复杂的正则解析和类型转换,效率极低。
   - `BufferedReader` 直接读取原始字节流,通过 `readLine()` 整行读取,减少 I/O 次数。

2. **StringTokenizer 分割字符串**:
   - 使用 `StringTokenizer` 分割字符串比 `split()` 更快,直接操作字符数组。
   - 通过 `nextToken()` 逐个获取分割后的字段,内存占用低。

3. **BufferedWriter 批量输出**:
   - `System.out.println` 每次调用都会触发 I/O,高频使用极慢。
   - `BufferedWriter` 会缓存输出内容,调用 `flush()` 时一次性写入,减少系统调用。

---

### 关键技巧与细节


#### 1. 异常处理简化
- 直接 `throws IOException` 避免 `try-catch`,竞赛代码允许这种写法。
- 生产代码不推荐,但竞赛中可极大简化代码。

#### 2. 高效读取不同类型数据
- **整数读取**:
  ```java
  int x = Integer.parseInt(st.nextToken());
  ```
- **长整型读取**:
  ```java
  long x = Long.parseLong(st.nextToken());
  ```
- **浮点数读取**:
  ```java
  double x = Double.parseDouble(st.nextToken());
  ```

#### 3. 处理多行输入
```java
// 读取 N 行数据
for (int i = 0; i < N; i++) {
    st = new StringTokenizer(br.readLine());
    // 处理每行数据...
}
```

#### 4. 输出优化
- 使用 `bw.write()` 代替 `System.out.println`。
- 注意手动添加换行符 `"\n"`。
- 最后必须调用 `bw.flush()` 确保输出全部内容。

---

### 性能对比
| 方法                   | 10^5次读取耗时 | 10^5次输出耗时 |
|-----------------------|---------------|---------------|
| `Scanner`             | ~1200ms       | ~800ms        |
| `System.out.println`  | -             | ~1500ms       |
| **本模板**            | **~200ms**    | **~100ms**    |

---

### 常见问题解决


#### Q1: 为什么读取数据时要用 `st.nextToken()`?
- 因为 `StringTokenizer` 会按空格分割字符串,逐个返回字段,避免一次性生成数组。

#### Q2: 如何处理没有明确行数的输入?
- 使用循环读取直到 `br.readLine()` 返回 `null`:
  ```java
  String line;
  while ((line = br.readLine()) != null) {
      // 处理每行数据...
  }
  ```

#### Q3: 需要读取单个字符怎么办?
- 直接读取整行后取字符:
  ```java
  char c = br.readLine().charAt(0);
  ```

---

掌握这个模板后,你的 Java 竞赛代码 I/O 效率将大幅提升,轻松应对百万级数据量的题目!

你可能感兴趣的:(蓝桥准备,java,开发语言)