使用List.clear()需要注意的一個坑

假設有這樣一個案例,我們有一個file文件,裏面有a b c d e f g h i j 一共10條數據,我們需要以4條爲一批,分成3批處理,即第一批是a b c d,第二批數據是e f g h,第三批數據是i j,所以樓主採用Map<Integer, List<TSSBBody>>方式裝數據,有多少批數據,就有多少個鍵值對,至於List的size(),必然是4 4 2,這種理解起來,比較簡單,所以開始擼代碼:

// 判斷一個file文件有多少行
	public long getLineNumber(File file) {
		if (file.exists()) {
			try {
				FileReader fileReader = new FileReader(file);
				LineNumberReader lineNumberReader = new LineNumberReader(fileReader);
				lineNumberReader.skip(Long.MAX_VALUE);
				long lines = lineNumberReader.getLineNumber() + 1;
				fileReader.close();
				lineNumberReader.close();
				return lines;
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		return 0;
	}

	public static void main(String[] args) throws InterruptedException {
		int TD_PileSize = 4;
		File file = new File("E:\\ceshi\\1.txt");
		long lineNumber = new Test().getLineNumber(file);
		int DataTotal = Integer.valueOf(String.valueOf(lineNumber));
		// 分多少批到特色獲取數據
		int pageCount = (int) Math.ceil((double) DataTotal / TD_PileSize);
		if (DataTotal == 0) {
			System.out.println("沒有需要推送的數據");
		}
		System.out.println("需要推送的數據有" + DataTotal + "條,分" + pageCount + "批推送!");

		InputStreamReader inputReader = null;
		BufferedReader bufferReader = null;
		OutputStream outputStream = null;
		try {
			InputStream inputStream = new FileInputStream(file);
			inputReader = new InputStreamReader(inputStream, "GBK");
			bufferReader = new BufferedReader(inputReader);

			// 讀取一行
			String line = null;
			Map<Integer, List<TSSBBody>> map = new HashMap<Integer, List<TSSBBody>>();
			List<TSSBBody> listBody = new ArrayList<TSSBBody>();
			if (DataTotal < TD_PileSize | DataTotal == TD_PileSize) {
				while ((line = bufferReader.readLine()) != null) {
					TSSBBody body = null; // 爲了不報錯 填null 實際是處理line 轉化爲對象
					listBody.add(body);
				}
				map.put(0, listBody);
			} else {
				int lineNum = 0;
				int mapNum = 0;
				while ((line = bufferReader.readLine()) != null) {
					lineNum++;
					TSSBBody body = null; // 爲了不報錯 填null 實際是處理line 轉化爲對象
					listBody.add(body);
					if (lineNum % TD_PileSize == 0) {
						mapNum++;
						map.put(mapNum - 1, listBody);
						System.err.println("清空前==" + listBody.size());
						listBody.clear();
					}
				}
				map.put(mapNum, listBody);
				System.out.println("mapNum============" + mapNum); //值=2

				for (Map.Entry<Integer, List<TSSBBody>> a : map.entrySet()) {
					System.err.println("" + a.getKey());
					for (TSSBBody ttBody : a.getValue()) {
						System.err.println(ttBody.toString());
					}
				}
			}

		} catch (IOException e) {
			System.out.println("" + e.getMessage());
		} finally {
			IOCloseUtil.closeAll(outputStream, bufferReader, inputReader);
		}

	}

其實代碼邏輯很好理解,當讀取4行倍數時,就執行賦值list,然後賦值給map,然後clear()重新賦值,理解起來覺得沒毛病,其實問題大了去了,執行代碼跟蹤,發現map遍歷,list的值都是i j,很奇怪是不是??

我們把listBody.clear()這句話改成:

listBody = null;

listBody = new ArrayList<TSSBBody>();

這兩句,問題就解決了。然後仔細一想,這不就是Java中的值引用還是對象引用的問題嗎?
值傳遞(形參類型是基本數據類型):方法調用時,實際參數把它的值傳遞給對應的形式參數,形式參數只是用實際參數的值初始化自己的存儲單元內容,是兩個不同的存儲單元,所以方法執行中形式參數值的改變不影響實際參數的值。

引用傳遞(形參類型是引用數據類型):也稱爲傳地址,方法調用時,實際參數是對象(或數組),這時實際參數與形式參數指向同一個地址,在方法執行中,對形式參數的操作實際上就是對實際參數的操作,這個結果在方法結束後被保留了下來,所以方法執行中形式參數的改變將會影響實際參數。

所以,通過clear方法清除之後再加入map,實際上所有的list都是同一個對象, 指向了同一個地址,這樣list裏的值就是最後一次加入list的元素,所以clear後,其實List自始至終都是通過一個對象,自然map的值都是i j。但是new不一樣,new的話就有多個對象,改這個list並不會影響另一個。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章