Java 流
-
流可以理解为一个数据,输入流表示从一个源读取数据,输出流表示向一个目标写数据
-
Java 为 I/O 提供了强大的而灵活的支持,使其更广泛地应用到文件传输和网络编程中
-
Scanner类 提供了一系列的方法,更加直观、易用性高,但 IO 流操作更加高效、灵活
1. 标准输入读取字符流
Java 的控制台输入由 System.in 完成
为了获得一个绑定到控制台的字符流,你可以把 System.in 包装在一个 BufferedReader 对象中来创建一个字符流
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
2. 输入流分词成为标记
StreamTokenizer类,用于解析基本类型的输入流,将其分解成标记(tokens),可以是数字、字符串、标识符等
StreamTokenizer in = new StreamTokenizer(br);
通过使用 StreamTokenizer,我们可以方便地读取和解析控制台输入的数据
StreamTokenizer 类 字段
StreamTokenizer 类 方法
根据需求循环获取对应Token:
// 只要没遇到终止符号(ctrl + D)就一直获取
while (in.nextToken() != StreamTokenizer.TT_EOF) {
// 这里根据需要循环 + nextToken()方法 获取需要的
// 数值类型
double n = in.nval;
// 字符类型
String str = in.sval;
}
获取之后,就可以编写具体的算法代码
3. 向标准输出写入字符流
为了向标准输出(这里指控制台)写入数据,我们使用 PrintWriter 类。
PrintWriter 类提供了一个简单的方法来输出数据,并且可以自动处理字符编码转换。
PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));
4. 输出到控制台
使用 PrintWriter 的 print 和 println 方法进行输出,一定要记得刷新输出流到控制台,代码最后需要关闭输出流。
// 输出
out.print();
out.println();
// 刷新和关闭输出流
out.flush();
out.close();
5. 用法示例
美团0810秋招第一题:
小美准备登录美团,需要输入密码,小美忘记了密码,只记得密码可能是 n个字符串中的一个。
小美会按照密码的长度从小到大依次尝试每个字符串,对于相同长度的字符串,小美随机尝试,并且相同的密码只会尝试一次。
小美想知道,她最少需要尝试多少次才能登录成功,最多需要尝试多少次才能登录成功。
小美不会重新尝试已经尝试过的字符串。成功登录后会立即停止尝试。
输入描述
第一行输入一个整数 n(1 <= n <= 1000)代表密码字符串的个数。
第二行输入一个只由小写字母组成的字符串 s(1<=|s|<=1000)代表正确的密码。
接下来 n 行,每行输入一个长度不超过 1000的字符串,代表小美记得的密码。
输出描述
在一行上输出两个整数,表示最少和最多尝试次数。
输入
4
ab
abc
ab
ac
ac
输出
1 2
public static void main(String[] args) throws IOException {
// 最基本最简单的输出方法 但是没有 IO 流高效
// Scanner scan = new Scanner(System.in);
// int nums = scan.nextInt(); // 字符串个数
// scan.nextLine(); // 消耗掉 nextInt 留下来的换行符
// String ans = scan.nextLine(); // 正确答案
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StreamTokenizer in = new StreamTokenizer(br);
PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));
// 相同的密码只会尝试一次 所以使用 集合 接收字符串
// 每种长度字符对应的哈希表:(字符长度 :字符集合)
Map<Integer, Set<String>> pos = new HashMap<>();
String ans = null; // 正确答案序列
while (in.nextToken() != StreamTokenizer.TT_EOF) {
int n = (int) in.nval;
in.nextToken();
ans = in.sval;
// 循环读取
for (int i = 0; i < n; i++) {
in.nextToken();
String str = in.sval;
// computeIfAbsent方法:如果指定的键不存在 则使用提供的映射函数新增,
// 如果键存在,则什么都不做
pos.computeIfAbsent(str.length(), k -> new HashSet<>()).add(str);
}
}
// 算法
int[] extracted = extracted(pos, ans);
out.print(extracted[0] + " " +extracted[1]);
out.flush(); // 记得刷新和关闭
out.close();
}
private static int[] extracted(Map<Integer, Set<String>> pos, String ans) {
// map 放入列表中, pos.entrySet() 返回 一个 Set集合
List<Map.Entry<Integer, Set<String>>> sortedPos = new ArrayList<>(pos.entrySet());
// 按照字符长度进行列表排序
sortedPos.sort(Map.Entry.comparingByKey());
// 遍历尝试 更新最小最大尝试次数
int step = 0;
int MIN = -1, MAX = -1;
for (Map.Entry<Integer, Set<String>> entry : sortedPos) {
Set<String> v = entry.getValue();
if (v.contains(ans)) {
// 这个长度下包含正确答案
MIN = step + 1; // 第一次就试到了正确答案
MAX = step + v.size(); // 所有字符都尝试一遍
} else {
// 这个长度下没有正确答案
step += v.size(); // 需要从短的开始 所以全部都要尝试一遍
}
}
return new int[]{MIN, MAX};
}