【Java】深入剖析Java输入输出的那些细节
前言
無論是新手入門的命令行輸入輸出,還是說OJ的讀入和輸出,離不開基本的IO操作。
但Java本身由于純粹的面向對象,輸入輸出不方便,所以我們倒不妨探討一番。
其實輸入輸出真實是所有學Java的入門者一定要先去學的,但用的越多越能深入地感受到輸入輸出或多或少的小問題。正所謂“養兵千日,用兵一時”,想要關鍵時刻一針見血,就必須在平時做好準備。
Are you ready? OK ~ 那今天我們就來聊聊,我們如何用好Java的輸入輸出。
看看Python
這叫輸入:
n=input()可以用eval()將輸入類型轉換成數值類型,用int()轉換成整數類型……
如下:
這叫輸出:
print(n)看看C++
cin可以輸入:
cin>>n;輸入還可以用scanf(),效率更高。
cout可以輸出:
cout<<n;輸出還可以用printf(),效率更高。
Java輸出篇
為什么先談輸出呢?
顯然因為簡單啊。
普通輸出流有三種輸出方式:
第一種是不換行輸出:
int n = 1; System.out.print(n);第二種是換行輸出:
System.out.println(n);第三種是格式化輸出:
System.out.printf("%d", n);還有錯誤輸出流,與上述內容類似:
第一種是不換行輸出:
System.err.print(n);第二種是換行輸出:
System.err.println(n);第三種是格式化輸出:
System.err.printf("%d", n);Java輸出的一些問題
異常是err錯誤流,普通輸出是out普通輸出流,可能會在IDE里由于線程的問題而混合在一起。
根據下面的源碼(java.lang.System),可知分別是InputStream、PrintStream對象,err流和out流是同一個類的不同對象。 public static final InputStream in;public static final PrintStream out;public static final PrintStream err;`
對于第五條,說明一下。我們先寫一個測試類,打一下時間戳:
public class PrintTest {public static void main(String[] args) {int[] array = new int[100000];long time1 = System.currentTimeMillis();for (int i = 0; i < array.length-1; i++) {System.out.print(array[i] + " ");}System.out.println(array[array.length-1]);long time2 = System.currentTimeMillis();StringBuilder builder = new StringBuilder();for (int i = 0; i < array.length; i++) {builder.append(array[i]).append(" ");}System.out.println(builder.toString().trim());long time3 = System.currentTimeMillis();System.out.println("策略1的輸出時間:" + (time2-time1));System.out.println("策略2的輸出時間:" + (time3-time2));} }除去上面的兩行輸出,會打印:
策略1的輸出時間:330
策略2的輸出時間:10
顯然,用StringBuilder而不是直接一次接一次的System.out.print(),是正解啦。
Java輸入篇
記得在學校剛學的時候,第一節課老師就讓大家寫一下輸入輸出語句,輸出還好,輸入就不行了,很多同學不會寫。其實我也是在學了很多語法以后才學會的輸入啦,下面就來說一說吧。
其實對初學者的一個困難就是輸出可以不必import,但輸入則不然,需要import java.io.*,或者import java.util.Scanner,可能不那么容易直接理解。
一般比較“出名”的處理方式是java.util.Scanner,這倒確實。
Scanner類可以直接讀取一下類型的對象:
- public String next() → 讀取下一個字符串(默認分隔符為Space or Tab or Enter)
- public String next?(String pattern) → 讀取下一個字符串(匹配到的串符合指定的正則表達式)
- public String next?(Pattern pattern) → 讀取下一個字符串(匹配到的串符合指定的正則表達式)
- public BigDecimal nextBigDecimal() → 讀取下一個高精小數(默認十進制)
- public BigInteger nextBigInteger() → 讀取下一個高精整數(默認十進制)
- public BigInteger nextBigInteger?(int radix) → 讀取下一個高精整數(指定進制)
- public boolean nextBoolean() → 讀取下一個布爾值
- public byte nextByte() → 讀取下一個byte整型數值(超容會報錯,默認十進制)
- public byte nextByte?(int radix) → 讀取下一個byte整型數值(超容會報錯,指定進制)
- public double nextDouble() → 讀取下一個雙精度浮點數值(默認十進制)
- public float nextFloat() → 讀取下一個單精度浮點數值(默認十進制)
- public int nextInt() → 讀取下一個int整型數值(超容會報錯,默認十進制)
- public int nextInt?(int radix) → 讀取下一個int整型數值(超容會報錯,指定進制)
- public String nextLine() → 讀取下一行內容以字符串類型返回(分隔符為Enter)
- public long nextLong() → 讀取下一個long整型數值(超容會報錯,默認十進制)
- public long nextLong?(int radix) → 讀取下一個long整型數值(超容會報錯,指定進制)
- public short nextShort() → 讀取下一個short整型數值(超容會報錯,默認十進制)
- public short nextShort?(int radix) → 讀取下一個short整型數值(超容會報錯,指定進制)
看吧,確實“全能”,不過我們在看到Scanner便捷的同時也應該清醒地認識到其性能的低下。
Scanner之所以能如此全能,依賴于Java支持的正則表達式,這點可以自行閱讀文檔或者源碼了解,這里不加詳細說明。
我們試想,比如一行這樣的數據:
1 2 555 333333 99 243 ……
如果說每一次都用scanner.nextInt(),就會慢太多。
反反復復的IO操作,每次都要判斷和處理,很影響效率的。
比如說在洛谷刷算法題的時候,我一般是用Java,但很多次都被卡死性能,不管怎么優化也不行,最后發現問題就在Scanner身上,那怎么處理呢?
答案是換BufferedReader,性能大幅提升。
看下面的代碼:
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in)); int num = Integer.parseInt(reader.readLine()); int[] record = new int[num]; String[] nums = reader.readLine().split("\\s+"); for (int i = 0; i < num; i++) {record[i] = Integer.parseInt(nums[i]); } reader.close();這樣一段代碼就可以先讀一個數值,再讀剩下一行里大量的數值。
看似還要開一個String[],但其實都說過了,再慢也慢不過IO,希望大家心里清楚最慢的就是IO了,減少IO是提升性能之道。
不過,在極其苛刻的情況下,Java是沒辦法完成的,嚴重TLE+MLE,哪怕你用了byte這樣的也不行,所以就得換C++了。畢竟算法題沒必要為Java設計性能指標,不達標也只能認了,Java比起C/C++太慢了。
Java輸入的一些問題
總結
以上是生活随笔為你收集整理的【Java】深入剖析Java输入输出的那些细节的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 详解Spring框架的AOP机制
- 下一篇: [计数排序]统计三个数和的[最大概率](