27.5.3 话单操作模块

话单操作主要涉及三个统计功能,即按照通话频率统计、按照通话时长统计和按照通话时段统计。本节主要介绍实现这些功能时涉及的算法,这些算法会通过具体的方法实现,至于较负载的算法如按照通话时间长度统计会辅以算法流程图。

27.5.3 话单操作模块 - 图1

图 27.12 打印文件对话框

1.按照通话频率统计

该功能要求对于用户选择的话单按照对方号码出现的次数统计,统计结果显示在新的话单中,这样新统计的话单中对方号码不会重复出现,并且新增加一个属性“通话频率”,如果用户选择了多个话单,程序会按照话单的顺序依次统计。其算法流程图如图27.13所示。

在实现统计分析时笔者设计了两个方法,一个方法为calculateFrequency(String[]string, JTable table),该方法有两个参数,第一个为号码数组,第二个为用户选择的话单对应的表格对象;另一个方法是processTableData(int[]index),该方法获得用户选择的话单,对每个话单进行统计,此时需要调用calculateFrequency(String[]string, JTable table)方法。

【实例27.9】下面介绍实现calculateFrequency(String[]string, JTable table)方法。


01 private void calculateFrequency(String[]string, JTable table){

02 for(int i=0;i<string.length;i++){

03 int count=0;

04 if(!string[i].equals("")){

05 String currentstring=string[i];

06 for(int j=i;j<string.length;j++){

07 if(string[j].equals(currentstring)){

08 string[j]="";

09 count=++count;

10 }

11 }

12 Object[]newdata=new Object[5];

13 newdata[0]=table.getValueAt(i,0);

14 newdata[1]=table.getValueAt(i,2);

15 newdata[2]=table.getValueAt(i,3);

16 newdata[3]=new Integer(count);

17 newdata[4]=table.getValueAt(i,8);

18 tablemodel.addRow(newdata);

19 }

20 }

21 }


27.5.3 话单操作模块 - 图2

图 27.13 按照通话频率统计算法流程图

【代码说明】该方法首先获得一个号码数组,找出第一个不为空的号码,在号码数组中搜索记录其出现次数,同时把数组中该号码的相应位置置空,把该号码、号码出现频率信息和与该号码相关的信息一起显示在新的话单上。继续搜索直到号码数组循环上述操作直到号码数组中没有号码为止。

【实例27.10】下面介绍实现processTableData(int[]index)方法,如下所示。


01 public void processTalbeData(int[]index){

02 for(int i=0;i<index.length;i++){

03 int tabindex=index[i];

04 JTable table=((JTable)window.shoujitablemap.get(Integer.toString(tabindex)));

05 int countrow=table.getRowCount();

06 String[]newdata=new String[countrow];

07 for(int j=0;j<countrow;j++){

08 newdata[j]=table.getValueAt(j,2).toString();

09 }

10 calculateFrequency(newdata, table);

11 }

12 }


【代码说明】方法首先获得用户选择的话单列表索引,即方法的参数,对于每一个话单,调用calculateFrequency(newdata, table)方法,来计算每个话单中号码出现的频率。

【运行效果】在按照频率统计时,用户获得的话单索引即为当前选择需要统计的话单在全部话单的列表中的位置。通话频率统计对话框如图27.14所示。

2.按照通话时长统计

27.5.3 话单操作模块 - 图3

图 27.14 通话频率统计对话框

该功能要求对于用户选择的话单按照通话时间长短进行统计,在统计前需要用户选择一个整型参数,如果选择2则表示统计该话单中同一号码通话时间最常和次长的记录,如果存在部分通话只用一个记录则相对应地选择一个记录,如果通话记录很多但是通话时间一样,则选择同样时长的多个记录。

该算法的思想是首先对用户选择的话单列表中的一个话单按照号码进行分类,并把分完类的号码表数据放入一个新的表,再调用一个方法处理该新表,首先对用户选择的时长参数数组进行排序,按照用户的时长参数选择符合参数要求的时长记录,并把该记录所在行的记录数据放置到一个新的表模型中,显示在用户主界面上。其算法流程图如图27.15所示。

在实现时长统计分析时,笔者设计了三个方法:

(1)第一个方法是processTalbeData(int[]index),该方法有一个参数,表示用户选择的话单列表索引。

(2)第二个方法是processTimeLength(String[]string, JTable table),该方法把当前话单中所有不同的对方号码进行分类,每类放入一个新的表模型存储,并调用第三个方法完成剩余的按照时长参数统计记录的功能。

27.5.3 话单操作模块 - 图4

图 27.15 按照通话时长统计算法流程图

(3)第三个方法是getRequiredRecord(JTable table, int timelength),该方法对第二个方法中的每一个新模型进行处理,首先是对时长属性列数据存入一个新的数组,并对该数据进行排序,按照时长参数的选择,选择符合的记录(如通话时间最长的记录、时间长度和排好序的数组中第一个时间长度相符的记录),最后把这些记录放置在用户界面上。

【实例27.11】下面介绍如何实现getRequiredRecord(JTable table, int timelength)方法。


01 private void getRequiredRecord(JTable table, int timelength){

02 int singletimecounter=0;

03 int countnew=0;

04 double[]singletime=new double[table.getRowCount()];

05 double currentdouble=0;

06 double[]timeshuzu=new double[table.getRowCount()];

07 for(int i=0;i<table.getRowCount();i++){

08 timeshuzu[i]=Double.parseDouble(table.getValueAt(i,5).toString());

09 }

10 for(int i=0;i<timeshuzu.length;i++){

11 if(!(timeshuzu[i]==0)){

12 countnew=0;

13 currentdouble=timeshuzu[i];

14 singletime[singletimecounter++]=currentdouble;

15 //此处用数组存储不同的时间长度值

16 for(int j=i;j<timeshuzu.length;j++){

17 if(timeshuzu[j]==currentdouble){

18 timeshuzu[j]=0;

19 countnew=++countnew;

20 }

21 }

22 }

23 }

24 //这里获得了同一长表上,相同号码的按时间排序的数组。下面完成了不同时间长度的排序

25 for(int h=0;h<singletime.length;h++){

26 this.bubble(singletime);

27 if(timelength>singletime.length){

28 timelength=singletime.length;

29 }

30 }

31 //singletime存储了不同的时间长度的值,需要对它排序

32 //这里获得了一个存储了同一号码表中不同的通话时间的一个数组。下面对数组排序,排序

33 //完按照timelength参数

34 //选择前几个时长,再在表中搜索相应的记录打到用户界面上

35 for(int z=0;z<timelength;z++){

36 double linshidouble=singletime[z];

37 for(int q=0;q<table.getRowCount();q++){

38 double time=Double.parseDouble(table.getValueAt(q,5).toString());

39 if(linshidouble==time){

40 Object[]newdata=new Object[table.getColumnCount()];

41 for(int p=0;p<table.getColumnCount();p++){

42 newdata[p]=table.getValueAt(q, p);

43 }

44 window.shoujimodel.addRow(newdata);

45 }

46 }

47 }

48 }


【代码说明】该方法的作用是对表模型中的记录,按照时长参数进行选择,并显示在用户界面上。第25~30行是相同号码按时间的排序。

【实例27.12】下面介绍实现processTimeLength()方法。


01 private void processTimeLength(String[]string, JTable table){

02 Object[]newdata=new Object[table.getColumnCount()];

03 for(int i=0;i<string.length;i++){

04 CellTableModel middleModel=new CellTableModel(new String[]{"d","b","c"});

05 JTable middletable=new JTable(middleModel);

06 int count=0;

07 if(!string[i].equals("")){

08 String currentstring=string[i];

09 for(int j=i;j<string.length;j++){

10 if(string[j].equals(currentstring)){

11 string[j]="";

12 count=++count;

13 for(int l=0;l<table.getColumnCount();l++){

14 newdata[l]=table.getValueAt(j, l);

15 }

16 //把该号码相关的所有记录放到了一张新表模型上(middletable)

17 middleModel.addRow(newdata);

18 }

19 }

20 //调用getRequiredRecord方法,并把两个参数middletable和paixupara传给该方法

21 getRequiredRecord(middletable, paixupara);

22 }

23 }

24 }


【代码说明】该方法对一个话单中不同的号码进行分类,每一类记录放置在一个表模型中。

【实例27.13】下面介绍实现processTalbeData()方法。


01 public void processTalbeData(int[]index){

02 for(int i=0;i<index.length;i++){

03 int tabindex=index[i];

04 //获得一个话单表对象

05 JTable table=((JTable)window.shoujitablemap.get(Integer.toString(

06 tabindex)));

07 if(table.getColumnName(6).equals("通话频率")){

08 return;

09 }

10 else{

11 int countrow=table.getRowCount();

12 String[]newdata=new String[countrow];

13 //把话单对象的时长存入一个新的数组

14 for(int j=0;j<countrow;j++){

15 newdata[j]=table.getValueAt(j,2).toString();

16 }

17 //调用processTimeLength方法对当前的话单表对象进行处理

18 processTimeLength(newdata, table);

19 }

20 }

21 if(window.shoujimodel.getRowCount()==0){

22 JOptionPane.showMessageDialog(null,"没有匹配数据");

23 }

24 }


【代码说明】该方法获得用户选择的话单列表索引,并对每一个话单表对象调用processTimeLength()方法。在按照频率统计时,用户获得的话单索引即为当前选择需要统计的话单在全部话单中的列表中的位置。

【运行效果】通话时长统计对话框如图27.16所示。

3.按照通话时段统计

该统计功能实现对一个或多个话单按照通话时间段选择记录,用户可以在时段统计对话框中选择起始和终止时间,这样就确定了一个时间段。该功能实现算法较简单,算法流程如图27.17所示。

27.5.3 话单操作模块 - 图5

图 27.16 通话时长统计对话框

27.5.3 话单操作模块 - 图6

图 27.17 按照通话时段统计算法流程图

【实例27.14】这里只介绍如何对一个话单实现按照时段统计,在程序中笔者定义了一个方法processDate(),该方法如下所示。


01 private void processDate(){

02 int indexlength=countindex.length;

03 //按手机号码处理

04 try{

05 for(int i=0;i<indexlength;i++){

06 int tableindex=countindex[i];

07 //获得要处理的话单表对象

08 JTable shoujitable=((JTable)window.shoujitablemap.get(Integer.toString(tableindex)));

09 if(shoujitable.getColumnName(6).equals("通话频率")){

10 return;

11 }

12 int countrow=shoujitable.getRowCount();

13 Object[]matchdata=new Object[shoujitable.getColumnCount()];

14 for(int j=0;j<countrow;j++){

15 Date currentdate=new Date(shoujitable.getValueAt(j,4).toString());

16 processCurrentDate(this.alldate, currentdate);

17 //判断如果当前话单的时长参数在时间段内则获取符合条件的记录,存入数组matchdata

18 if(currentdate.after(startdate)&&currentdate.before(enddate)||

19 currentdate.equals(startdate)||currentdate.equals(enddate)){

20 for(int l=0;l<matchdata.length;l++){

21 matchdata[l]=shoujitable.getValueAt(j, l);

22 }

23 //在用户主界面上增加一个新话单表显示符合时段条件的记录

24 window.shoujimodel.addRow(matchdata);

25 //每次向表模型添加数据后,需要更新数据视图

26 window.shoujitable.revalidate();

27 }

28 }

29 }

30 }

31 catch(Exception ex){

32 JOptionPane.showMessageDialog(null,"无法读取时间数据,类型不匹配");

33 window.tabpane.remove(window.counttab);

34 }

35 }


【代码说明】第8行获取要处理的话单表对象,第20~22行的matchdata数组中保存的是当前话单的时长参数在时间段内的记录。

【运行效果】在按照时段统计时,用户选择需要统计的话单,在时间段参数中选择时段,单击“确定”按钮则在程序主界面上显示统计结果。按照时段统计对话框如图27.18所示。

27.5.3 话单操作模块 - 图7

图 27.18 按照时段统计对话框

4.按属性排序

该功能要求单击打开话单的表头标题,则当前列按照属性类型排序,若是数字信息则按照数值大小排序,第一次单击按照升序排列,第二次单击按照降序排列,第二次单击恢复当初的原始序列。在主类MainWindow中有如下代码涉及表排序功能。


shoujimodel=new CellTableModel(teststring);

shoujistm=new SortedTableModel(shoujimodel);

shoujitable=new JTable(shoujistm);


类SortedTableModel是关键类,该类也是笔者编写的一个类,其构造函数接受一个表模型对象(DefaultTableModel)得到一个排序表模型对象,使用该排序表模型对象作为参数构造的JTable对象,可以实现表中单击表头标题则按照标题属性排序的功能。

【实例27.15】该类的实现代码如下所示。


01 import javax.swing.table.*;

02 import java.util.*;

03 public class SortedTableModel extends AbstractTableModel{

04 protected DefaultTableModel sourceModel;

05 protected int[]indexValues;

06 public SortedTableModel(DefaultTableModel model){

07 super();

08 sourceModel=model;

09 }

……//为了节省篇幅这里省略覆写抽象表模型的各种方法

10 public Class getColumnClass(int column){

11 return sourceModel.getColumnClass(column);

12 }

13 public int getSourceIndex(int index){

14 if(indexValues!=null){

15 return indexValues[index];

16 }

17 return-1;

18 }

19 //一旦用户单击相应header(表的头标题)就调用该方法完成行排序

20 public void sortRows(int column, boolean ascending){

21 int columnflag=column;

22 SortedItemHolder holder;

23 TreeSet sortedList=new TreeSet();

24 int count=getRowCount();

25 for(int i=0;i<count;i++){

26 holder=new SortedItemHolder(sourceModel.getValueAt(i, column),i);

27 sortedList.add(holder);

28 }

29 indexValues=new int[count];

30 Iterator iterator=sortedList.iterator();

31 int index=(ascending?0:count-1);

32 while(iterator.hasNext()){

33 holder=(SortedItemHolder)(iterator.next());

34 indexValues[index]=holder.position;

35 index+=(ascending?1:-1);

36 }

37 refreshViews();

38 }

39 public void clearSort(){

40 indexValues=null;

41 refreshViews();

42 }

43 public void refreshViews(){

44 fireTableDataChanged();

45 }

46 //SortedItemHolder类实现了Comparable接口

47 class SortedItemHolder implements Comparable{

48 public final Object value;

49 public final int position;

50 public SortedItemHolder(Object value, int position){

51 this.value=value;

52 this.position=position;

53 }

54 //覆盖并定义compareTo方法

55 public int compareTo(Object parm){

56 SortedItemHolder holder=(SortedItemHolder)parm;

57 Comparable comp=(Comparable)value;

58 int result=comp.compareTo(holder.value);

59 if(result==0){

60 result=(position<holder.position)?-1:1;

61 }

62 return result;

63 }

64 public int hashCode(){

65 return position;

66 }

67 //覆盖并定义equals方法

68 public boolean equals(Object comp){

69 if(comp instanceof SortedItemHolder){

70 SortedItemHolder other=(SortedItemHolder)comp;

71 if((position==other.position)&&(value==other.value)){

72 return true;

73 }

74 }

75 return false;

76 }

77 }

78 }


说明

如果读者需要实现和笔者程序中同样的对表格中不同属性的列排序的功能,可以直接使用该类(SortedTableModel)。

【代码说明】第20~38行的sortRows()方法实现单击表头标题后内容的排序。第47行的SortedItemHolder类实现了Comparable接口,所以它必须实现第55~63行的compareTo()方法。

【运行效果】图27.19给出排序的示例结果,对表格中通话“时长”属性进行降序排序,此时“时长(秒)”属性的左边出现一个带向下箭头的按钮,这样表格中每行数据按照通话时间从高到低排序,通话时间相同的按照在原始表格中出现的顺序而定。

27.5.3 话单操作模块 - 图8

图 27.19 按照“时长”属性降序排列表数据