目录
- 前言
- 正题
- 模板1
- 模板2
- 模板3
前言
可以不看,就唠唠嗑(滑稽)。
相信学习java的xdm(or jmm)都难逃学习算法的这一过程,但是接触算法的时候会发现,几乎所有的算法书都是以c/c++作为实现语言的。这是为什么呢?最主要的原因就是因为c/c++的执行速度是非常高的。
java相对于c/c++的执行速度是比较慢的,这是为什么呢?
因为java程序的执行依靠的是JVM(java虚拟机),而JVM是一个比较完善的虚拟机,由c++编写,它会自动进行内存管理防止内存泄漏等操作,这些操作也会花费时间。执行c/c++程序就不一样了,内存管理需要人为的写出代码,那如果我不去管它,就可以节省出这些时间。因为评价算法的指标是时间和空间复杂度,与内存的管理无关,所以我们完全可以省略掉这一步骤。
当然,原因不止这一个,这里就不一一阐述了,下面我们开始进入正题。
正题
对于java的输入输出,我相信大部分的小伙伴第一时间就是想到:
//输入
Scanner in=new Scanner(System.in);
int n=in.nextInt();
//输出
System.out.println(n);
这非常简单,为什么这么简单,那当然是因为底层做了很多你不知道的事。所以简单带来的负面影响就是效率低下。所有的测评环境对于这种效率低下的东西容忍度都是zero,因为它会卡时间。所以很多用java写代码的伙伴们就经常会看到”超出时间限制“这几个字,起初我也特别苦恼,但是后来通过学习,get到了一些高效的方式。见下文:
模板1
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.StringTokenizer;
public class Main{
public static void main(String[] args){
QuickInput in = new QuickInput();
PrintWriter out = new PrintWriter(System.out);
//in.nextInt().....
//out.println().....
out.close();//不关闭,就会有些数据留在缓冲区
}
}
class QuickInput
{
BufferedReader buf;
StringTokenizer tok;
QuickInput(){
buf = new BufferedReader(new InputStreamReader(System.in));
}
boolean hasNext(){
while(tok == null || !tok.hasMoreElements()){
try{
tok = new StringTokenizer(buf.readLine());
}catch(Exception e){
return false;
}
}
return true;
}
String next(){
if(hasNext()) return tok.nextToken();
return null;
}
int nextInt(){
return Integer.parseInt(next());
}
long nextLong(){
return Long.parseLong(next());
}
double nextDouble(){
return Double.parseDouble(next());
}
BigInteger nextBigInteger(){
return new BigInteger(next());
}
BigDecimal nextBigDecimal(){
return new BigDecimal(next());
}
}
这里自定义了一个类,模仿Scanner类。这样更加方便记忆。
使用的时候new一个对象调用对应方法便可。
注意:
1.空白字符是无法读入的,因为空白字符是作为默认的分隔符
2.可以自定义分隔符,创建StringTokenizer对象时使用构造方法:StringTokenizer(String str, String delim),参数delim为分隔符组成的字符串。例如new StringTokenizer(buf.readLine(),“ab”),那么分隔符为字符a和b
3.StringTokenizer类对参数字符串做划分与String类的split方法并不一样,StringTokenizer做划分的时候连续的分隔符并不会产生空字符串。
4.对于多组输入,可以在BufferedReader对象读取数据的时候进行判空来结束输入。
模板2
import java.io.*;
public class Main{
public static void main(String[] args) throws IOException{
BufferedReader in=new BufferedReader(new InputStreamReader(System.in));
BufferedWriter out=new BufferedWriter(new OutputStreamWriter(System.out));
//in.read(),in.readLine().....
//out.write()......
in.close();
out.close();//关流,不然有些数据还在缓冲区
}
}
模板2其实就是模板1的简化版,对于输入数据的单位是以行为单位的时候就可以使用模板2。并采用BufferedWriter类来进行输出,没有print系列方法,使用write系列方法进行输出
对于多组输入,同上:在BufferedReader对象读取数据的时候进行判空来结束输入。
模板3
import java.io.*;
public class Main{
public static void main(String[] args) throws IOException {
StreamTokenizer in = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));
PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));
//in.nextToken() :解析下一个标记
//in.nval :标记的double值
//例如:int a=(int)in.nval;
//或者:double b=in.nval;
//in.sval :标记的String值
//例如:String str=sval;
//如果当前的数据是数字,就通过in.nval字段获取对应值
//如果是字符串,就通过in.sval字段获取对应值
//例如:读取10个double型数字并打印
for(int i=0;i<10;++i){
//这里的nextToken方法是会抛出异常的,为了代码的简洁,就直接在main方法使用throws声明异常的抛出
in.nextToken();
out.println(in.nval);
}
out.close();
}
}
注意:
1.空白字符都是分隔符,默认只解析“字母,数字,空白字符,字符串引号中的内容和注释内容”,字母,数字以及引号中的内容被认为是标记的构成,空白字符为分隔符,注释内容不解析
2.以数字开头的标记是不能被解析为字符串的,例如:”45abc“,将会被解析成两个标记,分别为数字(45.0)、字符串(abc)。
3.其他不能解析的每个字符都会被解析为一个null
4.in.wordChars(int low,int hight) //新增标记的构成字符范围
5.in.quoteChar(int ch) //新增分割符
6.对于多组数据,可以判断in.nextToken()的返回值是否等于StreamTokenizer.TT_EOF便可,若等于说明到达流的末尾。
以上就是三个版本的快速IO方式,可以解决很大一部分的IO需求。但这仅仅只是模板,伙伴们可以根据自己实际的需求进行改进。
虽然以上模板不符合项目开发的要求,忽略了数据的安全性等等,但是对于算法测评来说,完全可以忽略这些。