一、题目描述
【注意】该题是华为2016年秋招机试题目,与牛客网中的HJ19略有不同,请看清题目要求,两题代码不通用。
开发一个简单错误记录功能小模块,能够记录出错的代码所在的1文件名称name、2行号row、3错误出现次数count。
要求:
(1)记录最多8条错误记录,对相同的错误记录(即文件名称name和行号row均相同)只记录一条,错误计数增加;(文件所在的目录不同,文件名和行号相同也要合并);
(2)超过16个字符的文件名称,只记录文件的最后有效16个字符(如果文件名不同,而只是文件名的后16个字符和行号相同,也不要合并);
(3)输入的文件可能带路径,记录文件名称不能带路径
数据范围:输入错误记录数量[1,1000],每条记录的长度[1,50] 。
1. 输入
若干行windows格式的文件路径字符串。每行包括带路径文件名称、行号,以空格隔开。
如:E:\V1R2\product\fpgadrive.c 1325
2. 输出
格式:文件名+代码行数+数目,以一个空格隔开。
按照数目从多到少排序,数目相同的情况下,按照输入时第一次出现的顺序排序。如果超过8条记录,则只输出前8条记录。
如: fpgadrive.c 1325 1
3. 样例
由于题目描述中只有上面的那个简单样例,因此笔者又自己创建了样例仅供参考。
输入:
A:\V1R2\product\mygoodjob.c 1325
A:\V1R2\product\mygoodjob.c 1325
B:\V1R2\product\abcdefghijklmnopqrst.exe 12
C:\V1R2\product\abcdefghijklmnopqrst.exe 12
A:\V1R2\product\moneymoney.c 1326
loveyouHW.html 521
loveyouHW.html 521
loveyouHW.html 521
loveyouHW.html 521
输出:
loveyouHW.html 521 4
mygoodjob.c 1325 2
ijklmnopqrst.exe 12 2
moneymoney.c 1326 1
二、思路分析
题目描述很乱,我们来梳理一下思路。
1. 关于错误
容易看出,每个错误有4个属性,分别是String name文件名、int row错误行数、int order首次出现的顺序、int count出现的次数。
其中,name文件名 + row错误行数,可以唯一地标识一个错误,相当于错误的ID。而order首次出现的顺序 + count出现次数用于对错误进行排序:当两个错误的count不一样时,count大的在前;而当count一样时,order小的(先出现)的在前。
那么,当初始化一个错误的时候,就应该应该赋予它name、row、order的值,并把count置为1;当再次遇到这个错误的时候,就把count++;为了使用name+row唯一标识一个错误,要重写hashcode和equals方法。由此我们就有了对错误类MyError的初步定义:
class MyError
{
private String name;
private int row;
private int order;
private int count;
MyError(String name, int row, int order){ // 初始化方法
this.name = name;
this.row = row;
this.order = order;
count = 1;
}
void addCount() { count++; } // 再次遇到时count++
int getCount() { return count;}
int getOrder() { return order;}
@Override
public int hashCode() {
// name是String类型自带hashCode方法,再加入row即可。掺杂name和row的方法比较灵活,只要能体现出用name+row标识错误即可
return row + name.hashCode();
}
@Override
public boolean equals(Object o) {
// hashCode一致的情况下,进一步比较name和row的内容,确保完全一样
if ( ((MyError) o).name.equals(name) && ((MyError) o).row==row )
return true;
return false;
}
}
2. 数据结构和输入
完成MyError的初步定义后,可以考虑数据结构和输入的问题了。选择的数据结构有3点要求:1错误记录的数量是未知的,因此要可变长;2最好能快速读取修改;3能允许我们按照MyError的count和order排序,因此选择了可变数组ArrayList。
ArrayList<MyError> errList = new ArrayList<>();
接下来就可以输入了,由于要记录错误出现的顺序,所以要引入变量order,初值为1,每读完一个错误就++。由此得到输入的大框架。
int order = 1;
while (sc.hasNext())
{
//*************
// 进行各种操作
//*************
order++;
}
现在填充中间的操作内容。先读文件名称name,由于题干中说 “ 输入的文件可能带路径 ” ,所以要加入判断。如果能在字符串中找到“\”则说明带路径,要从最后一个“\”的位置截取到字符串末尾,从而把干干净净的文件名拿出来;如果不带“\”则说明直接给出了文件名,可以不作处理使用。
String tempName = sc.next(); // 错误文件名
if (tempName.indexOf("\\")!=-1) // 有\就截取,没有就原封不动
tempName = tempName.substring(tempName.lastIndexOf('\\')+1,tempName.length());
然后读取错误行数row,直接调用一次sc.nextInt()即可,接着就可以实例化一个MyError对象了。
MyError tempErr = new MyError( tempName, sc.nextInt(), order );
最后检查新创建的对象tempErr是否是errList中已经保存过的错误,如果是则将其出现次数count++;否则作为新错误加入errList。刚刚定义MyError类时已经完成了addCount()方法,并重写了hashCode()和equals(),所以直接用就行了。
if ( errList.indexOf(tempErr) != -1 ) // 有则次数+1,无则新增
errList.get( errList.indexOf(tempErr) ).addCount();
else
errList.add( tempErr );
3. 排序
经过前两步的工作,错误记录的读取和errList的创建已经完成,现在只需要对errList中的对象按照count和order排序即可。这里使用Collections.sort()对errList这个ArrayList类型排序,重写其排序规则compare()。
Collections.sort(errList, new Comparator<MyError>() {
@Override
public int compare(MyError o1, MyError o2) { // 重写排序方式,计数相等就按照顺序;否则按照计数
return o2.getCount()==o1.getCount() ? o1.getOrder()-o2.getOrder() : o2.getCount()-o1.getCount() ;
}
});
4. 输出
该题对输出有2个数量上的要求:1是若超出8条记录,则只输出前8条;2是若文件名长度超过16,则只输出后16字符。
做到这里我们补充下之前定义过的MyError类,新增方法String getOutput()直接返回正确的输出String。当文件名超过16时进行截取再输出。
String getOutput()
{
if (name.length()<=16) // 判断如果长度超过16,就要截取
return name+" "+String.valueOf(row)+" "+String.valueOf(count);
else
{
String tempStr = name.substring(name.length()-16,name.length());
return tempStr+" "+String.valueOf(row)+" "+String.valueOf(count);
}
}
最后使用for循环输出结果,直接借助Math.min函数即可。如果超过了8条,min()函数结果为8;如果少于8条,min()函数结果为errList长度。避免了更多判断。
for ( int i=0; i<Math.min(8,errList.size()); i++ ) // 要么全部输出,要么输出8个,取决于Math.min的结果
System.out.println(errList.get(i).getOutput());
三、完整代码
笔者的完整源代码如下,可以直接提交通过该题。使用自定义类+ArrayList并不是最快最省空间的解法,但是思路清晰易懂、容易实现,仅供大家参考。如各位大神有更好的想法欢迎留言讨论,期待与您共同进步!
import java.util.*;
public class Main {
public static void main(String[] agrs)
{
Scanner sc = new Scanner(System.in);
ArrayList<MyError> errList = new ArrayList<>();
int order = 1;
while (sc.hasNext())
{
String tempName = sc.next(); // 错误文件名
if (tempName.indexOf("\\")!=-1) // 有\就截取,没有就原封不动
tempName = tempName.substring(tempName.lastIndexOf('\\')+1,tempName.length());
MyError tempErr = new MyError( tempName, sc.nextInt(), order++ ); // 设置错误文件名、错误行数、出现的顺序
if ( errList.indexOf(tempErr) != -1 ) // 有则次数+1,无则新增
errList.get( errList.indexOf(tempErr) ).addCount();
else
errList.add( tempErr );
}
Collections.sort(errList, new Comparator<MyError>() {
@Override
public int compare(MyError o1, MyError o2) { // 重写排序方式,计数相等就按照顺序;否则按照计数
return o2.getCount()==o1.getCount() ? o1.getOrder()-o2.getOrder() : o2.getCount()-o1.getCount() ;
}
});
for ( int i=0; i<Math.min(8,errList.size()); i++ ) // 要么全部输出,要么输出8个,取决于Math.min的结果
System.out.println(errList.get(i).getOutput());
}
}
class MyError
{
private String name;
private int row;
private int order;
private int count;
MyError(String name, int row, int order){
this.name = name;
this.row = row;
this.order = order;
count = 1;
}
String getOutput()
{
if (name.length()<=16) // 判断如果长度超过16,就要截取
return name+" "+String.valueOf(row)+" "+String.valueOf(count);
else
{
String tempStr = name.substring(name.length()-16,name.length());
return tempStr+" "+String.valueOf(row)+" "+String.valueOf(count);
}
}
void addCount() { count++; }
int getCount() { return count;}
int getOrder() { return order;}
@Override
public int hashCode() {
return row+name.hashCode();
}
@Override
public boolean equals(Object o) {
if ( ((MyError) o).name.equals(name) && ((MyError) o).row==row )
return true;
return false;
}
}