写在前面
离最后一次课程结束已经过去一周多了,忙碌于各种事情,终于找到一些琐碎的时间能来这里回忆与总结一下在这暑期短短两周的时间所学到的东西。由于时间过得有点久了,我会先回忆一下第二三次课程和作业,然后再说第四次作业和总结。
第二次课程
第二节课主要学习了接口(interface)的定义与实现(implement),介绍了容器类以及Java的IO。
接口
简单地讲,接口是拥有一些特定的相同方法的类的集合,而接口在定义的时候不会也不能去具体的实现这些方法,而是给出这些方法的类型和传入的参数等信息。如果一个类实现了某个接口,那么,这个类的一个对象就可以作为该接口的变量被申请。那么,要如何让一个类去实现一个接口呢?很简单,按接口的定义中声明的那样去具体实现特定的方法,然后在定义的时候加上implements+即可。比如我们在课堂上的一个例子。
public interface Geometry {
public double getVolume();
public String toString();
}
public class Box implements Geometry {
private double width;
private double length;
private double height;
private double scale;
private double vol;
public Box(double w, double l, double h,double s){
width = w;
length = l;
height = h;
scale = s;
vol = volume();
}
private double volume() {
// TODO Auto-generated method stub
return width*scale*height*scale*length*scale;
}
public double getVolume(){
return vol;
}
public String toString(){
return "Volume of Box:"+vol;
}
}
public class Cylinder implements Geometry {
private double height;
private double radius;
private double scale;
private double vol;
public Cylinder(double h, double r, double s){
height = h;
radius = r;
scale = s;
vol = volume();
}
private double volume() {
// TODO Auto-generated method stub
return Math.PI*radius*radius*height*scale*scale*scale;
}
public double getVolume(){
return vol;
}
public String toString(){
return "Volume of Cylinder:"+vol;
}
}
public class Sphere implements Geometry {
private double radius;
private double scale;
private double vol;
public Sphere(double r, double s){
radius = r;
scale = s;
vol = volume();
}
private double volume() {
// TODO Auto-generated method stub
return 4*Math.PI*radius*scale*scale*scale/3;
}
public double getVolume(){
return vol;
}
public String toString(){
return "Volume of Sphere:"+vol;
}
}
在上面这段代码中,Box,Cylinder和Sphere三个类都实现了接口Geometry声明的两个方法,因此这三个类的对象都可以看做Geometry的变量进行处理,减少了代码的重复与冗余,降低了编程复杂度。
容器类
Java中的容器类很像C++中的STL,是可以直接使用的一些数据结构。容器类主要分为两类:Collection(存放独立元素的序列)和Map(存放key-value型的元素对)。属于Collection类的容器有Vector和ArrayList等,属于Map类的容器有HashMap和TreeSet等。选择合适的容器类也可以简化代码,有时也能提高程序的运行效率。
输入输出流
使用BufferedReader可以很容易的实现从控制台或者是文本文件中读入,而使用BufferedWriter可以很容易的输出到控制台或者是文本文件中。但是需要注意的是使用这两种类型的对象的时候需要处理异常。
第二次作业
作业需求
定义集合接口类,并定义集合操作,包括:
(1) 能够对所管理的字符串进行字典序排序;
(2) 输入一个字符串,查找该字符串是否在集合中;
(3) 判断一个字符串集合是否当前集合的子集。
在此基础上通过接口实现机制来完成字符串集合类(使用容器类来管理字符串,且要管理的字符串数量不定),改造字符集合来实现集合这个接口类。针对字符串集合额外增加的功能包括:
(1) 输入一个字符集合,求它与 this 这个集合的交集;
(2) 查询给定单词在所有字符串中的出现次数;
(3) 增加输入功能,从文本文件输入初始字符串,构造字符串集合对象;
(4) 然后通过控制台来支持对集合的查询。
分析
除了要求查询单词在所有字符串中的出现次数之外并没有思维难度,而查询出现次数我们可以用KMP算法来做。
代码
public interface mySet {
public void sort();
public boolean search(String str);
public boolean isSubset(myStringSet anotherStringSet);
public myStringSet getInterSet(myStringSet anotherStrSet);
public int count(String str);
}
import java.io.*;
import java.util.*;
public class myStringSet implements mySet {
private Vector<String> strSet;
public myStringSet(){
strSet=new Vector<String>();
}
public myStringSet(Vector<String> StringSet){
strSet=new Vector<String>();
for(int i=0;i<StringSet.size();++i){
boolean isUnique=true;
for(int j=0;j<strSet.size();++j)if(StringSet.elementAt(i).equals(strSet.elementAt(j)))isUnique=false;
if(isUnique)strSet.add(StringSet.elementAt(i));
}
}
private void sort(int l,int r){
if(l+1==r)return;
int mid=(l+r)/2;
sort(l,mid);
sort(mid,r);
Vector<String>tmp=new Vector<String>();
int p=l,q=mid;
while(p<mid||q<r){
if(q>=r||(p<mid&&strSet.elementAt(p).compareTo(strSet.elementAt(q))<=0))tmp.add(strSet.elementAt(p++));
else tmp.add(strSet.elementAt(q++));
}
for(int i=0;i<tmp.size();++i)strSet.setElementAt(tmp.elementAt(i),l+i);
}
private int find(String str){
for(int i=0;i<strSet.size();++i)if(strSet.elementAt(i).equals(str))return i;
return -1;
}
private boolean OutOfBounds(int index){
return index>=0&&index<=strSet.size();
}
private int[] KmpInit(String str){
int[] f=new int[str.length()];
f[0]=-1;
for(int i=1,j;i<str.length();++i){
for(j=f[i-1];j!=-1&&str.charAt(j+1)!=str.charAt(i);j=f[j]);
f[i]=str.charAt(j+1)==str.charAt(i)?j+1:0;
}
return f;
}
private int Kmp(String str){
int[] f=KmpInit(str);
int res=0;
for(int i=0;i<strSet.size();++i){
String s=strSet.elementAt(i);
for(int j=0,k=-1;j<s.length();++j){
for(;k!=-1&&str.charAt(k+1)!=s.charAt(j);k=f[k]);
if(str.charAt(k+1)==s.charAt(j)){
++k;
if(k+1==str.length()){
++res;
k=f[k];
}
}
}
}
return res;
}
public String myToString(){
String res=new String();
for(int i=0;i<strSet.size();++i)res+=strSet.elementAt(i)+"\n";
return res;
}
public void sort(){
if(strSet.size()>1)sort(0,strSet.size());
}
public boolean search(String str){
return find(str)>=0;
}
public boolean isSubset(myStringSet anotherStrSet){//返回anotherStrSet是否是当前集合的子集
for(int i=0;i<anotherStrSet.strSet.size();++i)if(find(anotherStrSet.strSet.elementAt(i))<0)return false;
return true;
}
public void insert(String str,int index){
if(find(str)>=0){
System.out.println("string "+str+" exists!");
return;
}
if(OutOfBounds(index)){
System.out.println("Out of bounds!");
return;
}
strSet.insertElementAt(str, index);
}
public void append(String str){
if(find(str)>=0){
System.out.println("string "+str+" exists!");
return;
}
strSet.add(str);
}
public void delete(String str){
int index=find(str);
if(index<0){
System.out.println("string "+str+" can not be found!");
return;
}
strSet.remove(index);
}
public myStringSet getInterSet(myStringSet anotherStrSet){
myStringSet res=new myStringSet();
for(int i=0;i<strSet.size();++i)if(anotherStrSet.search(strSet.elementAt(i)))res.append(strSet.elementAt(i));
return res;
}
public int count(String str){
return Kmp(str);
}
public void init(String path){
//从控制台输出字符串集合来初始化字符串集合,不同串以换行符'\n'分割,不允许为空
FileReader fin=null;
strSet=new Vector<String>();
try{
fin=new FileReader(path);
}
catch(Exception e){
System.out.println(e.toString());
}
finally{
if(fin==null)return;
try{
char[] input=new char[20000];
int len=fin.read(input);
if(len==-1){
System.out.println("The file is empty!");
return;
}
int i=0;
while(true){
while(i<len&&(input[i]=='\n'||input[i]=='\r'))++i;
if(i==len)break;
String str=new String();
while(i<len&&input[i]!='\n'&&input[i]!='\r')str+=input[i++];
append(str);
if(i==len)break;
}
}
catch(Exception e){
System.out.println(e.toString());
}
try{
fin.close();
}
catch(Exception e){
System.out.println(e.toString());
}
}
}
public void search(){
//从控制台读入若干个字符串,不同串以换行符分割,不允许为空串
//当读到"cmd:search complete"时,停止读入
BufferedReader fin=null;
try{
fin=new BufferedReader(new InputStreamReader(System.in));
}
catch(Exception e){
System.out.println(e.toString());
return;
}
finally{
String str=null;
while(true){
try{
str=fin.readLine();
}
catch(Exception e){
System.out.println(e.toString());
break;
}
finally{
if(str.equals("cmd:search complete"))break;
if(str.equals(""))continue;
if(find(str)>=0)System.out.println("string "+str+" exists!");
else System.out.println("string "+str+" can not be found!");
}
}
/*加上这句会有bug:不能多次调用此函数
try{
fin.close();
}
catch(Exception e){
System.out.println(e.toString());
}
*/
}
}
}
第三次作业
作业要求
实现一个基于文本的词频统计程序。要求能够从文本文件(规模未知)输入字符串,构造字符串集合,在字符串集合之外单独管理分割出的单词以及该单词出现次数和位置。
(1) 要求重构一个 TokenString 类,该类保存某个单词的信息,包括但不限于单词内容、单词位置信息和单词在文本中出现的次数;
(2) 要求实现一个 TokenManager 类,该类存储所有的 TokenString ,并至少记录包含的 TokenString 总数;
(3) 文本中不会出现汉字;
分析
用正则表达式 "[^a-zA-Z0-9]+" 分割出所有的单词,然后用 HashMap 和 Vector 维护单词和出现的位置。
代码
import java.util.*;
public class TokenString {
private String str;
private int index;
public TokenString(){
str=new String("");
index=-1;
}
public TokenString(String Str,int Index){
str=Str;
index=Index;
}
public String getStr(){
return str;
}
public int getIndex(){
return index;
}
}
public class Position {
private int r,c;
public Position(){
r=c=-1;
}
public Position(int R,int C){
r=R;
c=C;
}
public void set(int R,int C){
r=R;
c=C;
}
public int getRow(){
return r;
}
public int getColume(){
return c;
}
public String myToString(){
return "{"+r+","+c+"}";
}
}
import java.util.*;
public class Positions {
private Vector<Position>pos;
public Positions(){
pos=new Vector<Position>();
}
public int getCount(){
return pos.size();
}
public void add(Position Pos){
pos.add(Pos);
}
public Vector<Position>getPos(){
return pos;
}
}
import java.io.*;
import java.util.*;
public class TokenManager {
private HashMap<String,Integer>map;
private Vector<Positions>pos;
public TokenManager(){
map=new HashMap<String,Integer>();
pos=new Vector<Positions>();
}
public boolean search(String str){
return map.containsKey(str);
}
public void updateToken(String str,Position Pos){
if(str.equals(""))return;
if(!search(str)){
map.put(str,pos.size());
pos.add(new Positions());
}
int index=map.get(str);
pos.elementAt(index).add(Pos);
}
public void getWordFrequencyCount(String path) throws IOException{
BufferedWriter fout=new BufferedWriter(new FileWriter(path));
Vector<TokenString>tmp=new Vector<TokenString>();
Iterator ite=map.entrySet().iterator();
while(ite.hasNext()){
Map.Entry<String,Integer> cur=(Map.Entry<String,Integer>)ite.next();
tmp.add(new TokenString(cur.getKey(),cur.getValue()));
}
Collections.sort(tmp,new cmp());
ite=tmp.iterator();
while(ite.hasNext()){
TokenString cur=(TokenString)ite.next();
Vector<Position> temp=pos.elementAt(cur.getIndex()).getPos();
fout.write(cur.getStr()+":"+temp.size()+":{");
fout.flush();
boolean first=true;
for(int i=0;i<temp.size();++i){
if(!first)fout.write(",");
first=false;
fout.write(temp.elementAt(i).myToString());
fout.flush();
}
fout.write("}\r\n");
fout.flush();
}
fout.close();
}
public int getCount(){
return map.size();
}
}
import java.io.*;
public class Main {
public static void main(String[] args) throws IOException{
long startTime=System.currentTimeMillis();
//System.out.println(startTime);
BufferedReader fin=null;
fin=new BufferedReader(new FileReader("H:\\article.txt"));
TokenManager tokenManager=new TokenManager();
int row=0;
String tmp=null;
while((tmp=fin.readLine())!=null){
String[] words=tmp.split("[^a-zA-Z0-9]+");
for(int i=0,temp=-1;i<words.length;++i){
temp=tmp.indexOf(words[i],temp+1);
Position pos=new Position(row,temp);
tokenManager.updateToken(words[i],pos);
temp+=words[i].length()-1;
}
++row;
}
if(fin!=null)fin.close();
tokenManager.getWordFrequencyCount("H:\\output.txt");
long endTime=System.currentTimeMillis();
//System.out.println(endTime);
System.out.println(endTime-startTime);
}
}
一开始写的时候将所有要输出的内容都加在一起,最后一次性输出一个超级长的字符串,因此程序的输出效率非常低。后来课后改了一下,将输出的内容分很多次输出,每次输出一点之后用 BufferedWriter 的 flush 方法刷新一下,这样程序就快了很多。
第四次作业
作业要求
实现一个基于词频统计的二元短语分析:
1.统计二元短语的出现频度,即统计任意两个连续单词 < Wi , Wj > 构成的短语在文章中出现的次数,记所有短语出现的次数总和为 N;
2.令 P(Wi|W) = #< W , Wi > / #< W >,其中 #< W , Wi > 为短语 < W , Wi > 在文章中出现的次数,#< W > 为 W 在文件中出现的总次数。
3.控制台随机输入一个单词(不含有分隔符)W,输出 P(Wi|W) 值前五的短语 < W , Wi >;
4.使用 hashcode 比较单词是否相等,并使用 hashmap 进行管理。
分析
逐行处理读入的文章,用 split 方法处理掉所有可能出现的分隔符,然后没有被分隔符隔开的单词之间依次建立二元短语关系。由于要対短语的出现次数排序,所以我们需要去写一个类来实现对应类型的 Comparator。
代码
import java.util.*;
public final class TokenString {
private String Word;
private int cnt;
private HashMap<String,Integer>phrase;
private Vector<Pair>v;
public TokenString(){
Word=new String("");
cnt=0;
phrase=new HashMap<String,Integer>();
v=new Vector<Pair>();
}
public void setWord(String word){//设置单词
Word=word;
}
public String getWord(){//查询单词
return Word;
}
public void addCnt(){//增加出现次数
++cnt;
}
public int getCnt(){//查询出现次数
return cnt;
}
public void addPhraseCnt(String str){//增加二元短语Word+str的出现次数
int tmp=0;
if(phrase.containsKey(str)){
tmp=phrase.get(str);
}
phrase.put(str, tmp+1);
}
public int getPhraseCnt(String str){//查询二元短语Word+str的出现次数
if(!phrase.containsKey(str))return 0;
return phrase.get(str);
}
public double getPhraseFrequency(String str){//查询二元短语Word+str的出现频率
return (double)getPhraseCnt(str)/cnt;
}
public void sort(){//对所有短语进行排序
Iterator it;
it=phrase.entrySet().iterator();
while(it.hasNext()){
Map.Entry<String,Integer> cur;
cur=(Map.Entry<String,Integer>)it.next();
v.add(new Pair(cur.getKey(),cur.getValue()));
}
Collections.sort(v,new PairCmp());
}
public void getFirstPhrases(){//查询出现次数最多的至多前五个短语及频率
for(int i=0;i<5&&i<v.size();++i){
Pair cur=v.elementAt(i);
System.out.printf("%s %s: %.6f\n",Word,cur.getWord(),(double)cur.getCnt()/cnt);
}
}
}
//将单词和出现次数包装为一个二元组,方便排序
public final class Pair {
private String Word;
private int cnt;
public Pair(){
Word=new String("");
cnt=0;
}
public Pair(String str,int n){
Word=str;
cnt=n;
}
public String getWord(){
return Word;
}
public int getCnt(){
return cnt;
}
}
//实现Caparator接口来对Pair进行排序
import java.util.*;
public final class PairCmp implements Comparator<Pair> {
public int compare(Pair x,Pair y){
int x_cnt=x.getCnt();
int y_cnt=y.getCnt();
if(x_cnt==y_cnt){
String sx=x.getWord(),sy=y.getWord();
if(sx.hashCode()==sy.hashCode()&&sx.equals(sy))return 0;
return sx.compareTo(sy);
}
return x_cnt > y_cnt?-1:1;
}
}
import java.util.*;
public final class TokenManager {
private Vector<TokenString>V;
private HashMap<String,Integer>M;
private int cnt;
public TokenManager(){
V=new Vector<TokenString>();
M=new HashMap<String,Integer>();
cnt=0;
}
public void addCnt(){//增加单词的出现次数
++cnt;
}
public int getCnt(){//查询单词的出现次数
return cnt;
}
public void addWord(String str){//增加单词 str 的出现次数
int index;
if(M.containsKey(str))index=M.get(str);
else{
index=V.size();
V.add(new TokenString());
V.elementAt(index).setWord(str);
M.put(str, index);
}
V.elementAt(index).addCnt();
addCnt();
}
public void addPhrase(String str1,String str2){//增加短语 str1+str2 的出现次数
int index=M.get(str1);
V.elementAt(index).addPhraseCnt(str2);
}
public int getWordCnt(String str){//查询单词 str 的出现次数
if(!M.containsKey(str))return 0;
return V.elementAt(M.get(str)).getCnt();
}
public int getPhraseCnt(String str1,String str2){//查询二元短语 str1+str2的出现次数
if(!M.containsKey(str1))return 0;
int index=M.get(str1);
return V.elementAt(index).getPhraseCnt(str2);
}
public double getPhraseFrequency(String str1,String str2){//查询二元短语 str1+str2的出现频率
if(!M.containsKey(str1))return 0;
int index=M.get(str1);
return V.elementAt(index).getPhraseFrequency(str2);
}
public void sort(){//对短语进行排序
for(int i=0;i<V.size();++i)V.elementAt(i).sort();
}
public void getFirstPhrasesOf(String str){//查询单词 str 的出现次数最多前五个短语
if(!M.containsKey(str)){
System.out.println("Word "+str+" does not exist!");
return;
}
int index=M.get(str);
V.elementAt(index).getFirstPhrases();
}
}
import java.io.*;
import java.util.*;
public final class Main {
public static void main(String[] args) throws IOException{
long startTime=System.currentTimeMillis();
//int cnt=0;
BufferedReader fin=new BufferedReader(new FileReader("H:\\10.txt"));
//从文件里读入文章,初始化单词和短语
TokenManager tkM=new TokenManager();
String currentLine=null;
while((currentLine=fin.readLine())!=null){
String[] division=currentLine.split(",|\\\\|\\.|/|:|\\+|\\-|\\=|\\||\"|\'|\\?|\\!|\\~|\\<|\\>|\\[|\\]|\\{|\\}|\\(|\\)|\\@|\\#|\\$|\\%|\\^|\\&|\\*|\\_");
//逗号,反斜杠\,句点.,斜杠/,冒号:,加号+,减号-,等号=,竖线|,双引号",单引号',问号?,感叹号!,取反符号~,括号家族<>,[],{},(),井号#,at@,美元符号$,百分号%,异或符号^,且&,乘号*,下划线_
for(int i=0;i<division.length;++i){
//System.out.println(division[i]);
String[] word=division[i].split("[^a-zA-Z0-9]+");
for(int j=0;j<word.length;++j){
if(word[j].equals(""))continue;
tkM.addWord(word[j]);
if(j+1<word.length){
tkM.addPhrase(word[j], word[j+1]);
}
//++cnt;
//System.out.println(cnt+": "+word[j]);
}
}
}
fin.close();
tkM.sort();
long endTime=System.currentTimeMillis();
System.out.println("初始化完成,用时为:"+(endTime-startTime)+" ms");
fin=new BufferedReader(new InputStreamReader(System.in));
//从控制台输入待查询的单词,输出前五个短语
while((currentLine=fin.readLine())!=null){
String[] str=currentLine.split("[^a-zA-Z0-9]+");
for(int i=0;i<str.length;++i){
if(str[i].equals(""))continue;
tkM.getFirstPhrasesOf(str[i]);
}
}
}
}
总结
通过这两周多的学习,我更加深入的了解了 Java 的常用语法,也提升了自己的代码能力,受益匪浅。虽然课程已经告一段落,但是我在今后的空闲时间中,一定会继续钻研 Java 和面向对象编程的思想。感谢老师和助教的辛勤付出。