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