使用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并不会影响另一个。

发布了194 篇原创文章 · 获赞 20 · 访问量 5万+

猜你喜欢

转载自blog.csdn.net/weixin_39309402/article/details/103983323