欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

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

程序员文章站 2022-05-05 09:39:31
...

假设有这样一个案例,我们有一个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,很奇怪是不是??

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

我们把listBody.clear()这句话改成:

listBody = null;

listBody = new ArrayList<TSSBBody>();

这两句,问题就解决了。然后仔细一想,这不就是Java中的值引用还是对象引用的问题吗?
值传递(形参类型是基本数据类型):方法调用时,实际参数把它的值传递给对应的形式参数,形式参数只是用实际参数的值初始化自己的存储单元内容,是两个不同的存储单元,所以方法执行中形式参数值的改变不影响实际参数的值。

引用传递(形参类型是引用数据类型):也称为传地址,方法调用时,实际参数是对象(或数组),这时实际参数与形式参数指向同一个地址,在方法执行中,对形式参数的操作实际上就是对实际参数的操作,这个结果在方法结束后被保留了下来,所以方法执行中形式参数的改变将会影响实际参数。

所以,通过clear方法清除之后再加入map,实际上所有的list都是同一个对象, 指向了同一个地址,这样list里的值就是最后一次加入list的元素,所以clear后,其实List自始至终都是通过一个对象,自然map的值都是i j。但是new不一样,new的话就有多个对象,改这个list并不会影响另一个。

相关标签: 基础 Error