【Java学习笔记】46:鼠标监听做动态分区分配的FF/BF/WF算法演示
来源:互联网 发布:淘宝差评有什么后果 编辑:程序博客网 时间:2024/05/21 17:56
操作系统课的上机题,用JFrame做了一个可视化的演示程序,这次新用到了鼠标监听的知识。
Main.java
//主类public class Main { static int MAXN=20;//一共20大小的内存 static int k=0;//1:FF/2:BF/3:WF选择算法 static String sf=null;//用来拼接给JFrame的标题 public static void main(String[] args) { Input ip=new Input();//输入窗口 while(0==Main.k)//等待输入条件成立 { //在我这个jdk下必须要做事情而不能用空语句 //不要使用println,可以观察下控制台右边的下拉栏 //在以前做棋盘覆盖时候竟然没发现这件事 System.out.print(""); } //用来拼接给JFrame的标题 switch(Main.k) { case 1: Main.sf="FF首次适应"; FF f=new FF(); f.Show(); break; case 2: Main.sf="BF最佳适应"; BF b=new BF(); b.Show(); break; case 3: Main.sf="WF最坏适应"; WF w=new WF(); w.Show(); break; default: Main.sf="程序逻辑有问题!"; break; } //System.out.println(Main.k); }}
TwoList.java
这个类抽取了三个算法演示中共同的成分,是这个程序的核心部分。
import java.awt.Color;import java.awt.Container;import java.awt.event.ActionListener;import java.awt.event.KeyEvent;import java.awt.event.KeyListener;import java.awt.event.MouseEvent;import java.awt.event.MouseListener;import java.util.Collections;import java.util.Comparator;import java.util.LinkedList;import java.util.List;import java.util.Random;import javax.swing.JButton;import javax.swing.JFrame;import javax.swing.JLabel;import javax.swing.JPanel;import javax.swing.JTextField;//三种算法的共同父类abstract class TwoList extends JFrame implements ActionListener,MouseListener{ private int NN=3;//初始化时候要插入的块数 private int NZ=1;//初始化时每个块占用的格子数 Container con;//顶层容器 JPanel jp;//中间容器 JLabel title;//大标题 JLabel[] mm;//模拟主存的数组 JTextField tf;//文本框 JButton jb;//按钮 int MX=-1,MY=-1;//鼠标位置 //FF算法用到的数据结构 List<Job> fll=new LinkedList<Job>();//满链 List<EPTBlock> ept=new LinkedList<EPTBlock>();//空闲链 //Queue<Job> fll=new PriorityQueue<Job>(Main.MAXN,cmp);//满队 //有关Job地址的匿名比较器 public static Comparator<Job> cmpj = new Comparator<Job>(){ @Override public int compare(Job i, Job j) { return (i.JAdd-j.JAdd);//这样优先级队列中是地址小的先出 } }; //有关EPTBlock地址的匿名比较器 public static Comparator<EPTBlock> cmpe = new Comparator<EPTBlock>(){ @Override public int compare(EPTBlock i, EPTBlock j) { return (i.EAdd-j.EAdd);//这样优先级队列中是地址小的先出 } }; //自己用的:按地址插入 private boolean AdCheck(int A,int L){ //遍历整个空闲链 for(EPTBlock e:ept) { //若A和L都没问题,即块落在这个范围 if(A>=e.EAdd && A+L<=e.EAdd+e.ELen) { //对满链的操作 Job j=new Job(A,L); fll.add(j); //对空闲链的操作 ept.remove(e);//先将e移除 //如果A比块起始地址大,肯定有个左边小块 if(A>e.EAdd) ept.add(new EPTBlock(e.EAdd,A-e.EAdd)); //如果末地址没到块末,肯定有个右边小块 if(A+L<e.EAdd+e.ELen) ept.add(new EPTBlock(A+L,e.EAdd+e.ELen-A-L)); return true;//返回true表示成功插入 } } return false; }//End 按地址插入 //构造器 TwoList(){ /*①JFrame窗体名*/ super(Main.sf+"算法演示"); /*②随机插入一些小Job形成分区*/ ept.add(new EPTBlock(0,Main.MAXN)); //随机数的最大最小值 int rdmax=Main.MAXN; int rdmin=0; //随机数生成器 Random rd=new Random(); //插入NN个小Job,长度都是NZ,用NN的减少确认插入了NN次 while(0!=this.NN){ //生成整个范围内的随机数s表示地址 int s=rd.nextInt(rdmax)%(rdmax-rdmin+1)+rdmin; //按地址插入 if(this.AdCheck(s,this.NZ)==true) this.NN--;//仅当成功插入时才减少 } /*③构造并显示最初的窗口*/ setDefaultCloseOperation(EXIT_ON_CLOSE);//按关闭时:退出 setLocation(200,130);//位置 setSize(650,300);//尺寸// setExtendedState(MAXIMIZED_BOTH);//扩展状态:最大化 //初始化顶层容器Container和中间容器JPanel con=getContentPane();//顶层容器直接从JFrame获得 jp=new JPanel();//初始化一个中间容器JPanel //布局设为空,这样JLabel才能用setBounds jp.setLayout(null); //大标题 title=new JLabel(Main.sf+"算法演示"); title.setFont(new java.awt.Font("Dialog",1,30));//字体,加粗,字号 title.setBounds(185,10, 350, 50);//位置和大小(横纵横纵) title.setForeground(Color.blue);//颜色 //为模拟主存的数组分配空间 mm=new JLabel[Main.MAXN]; for(int i=0;i<Main.MAXN;i++) { mm[i]=new JLabel(); mm[i].setBounds(600/Main.MAXN*i+25, 80, 600/Main.MAXN, 100); mm[i].setOpaque(true);//设置非透明 /* //设置绿颜色表示空闲,交叉以区分 mm[i].setBackground(new Color(0x66BB6A));*/ jp.add(mm[i]);//嵌套给中间容器JPanel } //文本框和按钮 tf=new JTextField(); tf.setFont(new java.awt.Font("Dialog",1,30)); tf.setBounds(200, 200, 100, 50); jb=new JButton("尝试插入"); jb.setBounds(350, 200, 100, 50); /*键盘事件:判断键入的是否为数字*/ tf.addKeyListener(new KeyListener() { @Override public void keyTyped(KeyEvent e) { int temp = e.getKeyChar();//ASCII码 if(temp == 10){//按回车时 } else if(temp != 8)//没有按backspace时 { //下面检查是不是在0~9之间 if(temp>57){ e.consume();//如果不是则消除key事件,也就是按了键盘以后没有反应; }else if(temp<48){ e.consume(); } } } @Override public void keyReleased(KeyEvent e) { // TODO Auto-generated method stub } @Override public void keyPressed(KeyEvent e) { // TODO Auto-generated method stub } }); //为按钮添加动作响应 jb.addActionListener(this); //为JPanel添加鼠标事件 jp.addMouseListener(this); //继续做容器嵌套 jp.add(tf); jp.add(jb); jp.add(title); con.add(jp); setVisible(true);//设置可见 }//End 构造器 //显示两个链表 void Show(){ //打印前两个List按地址排序,以保证奇偶交叉着色 Collections.sort(fll,cmpj); Collections.sort(ept,cmpe); //打印到控制台一份,又显示在界面上一份 System.out.println("满链:"); int i=0; for(Job j:this.fll) { System.out.print(j+","); i++; if(i%2==0)//奇偶交叉着色 { for(int k=j.JAdd; k<j.JAdd+j.JLen; k++) { //设置颜色 mm[k].setBackground(new Color(0xE91E63)); } } else for(int k=j.JAdd; k<j.JAdd+j.JLen; k++) { //设置相似但能区分的颜色 mm[k].setBackground(new Color(0xEF5350)); } } System.out.println(""); System.out.println("空闲链:"); i=0; for(EPTBlock e:this.ept) { System.out.print(e+","); i++; if(i%2==0)//奇偶交叉着色 { for(int k=e.EAdd; k<e.EAdd+e.ELen; k++) { //设置颜色 mm[k].setBackground(new Color(0x1E88E5)); } } else for(int k=e.EAdd; k<e.EAdd+e.ELen; k++) { //设置相似但能区分的颜色 mm[k].setBackground(new Color(0x5C68C0)); } } System.out.println(""); /* for(int i=0;i<fll.size();i++) { if(i%2==0)//奇偶交叉着色 { for(int j=fll.get(i).JAdd; j<fll.get(i).JAdd+fll.get(i).JLen; j++) { //设置颜色 mm[j].setBackground(new Color(0xE91E63)); } } else for(int j=fll.get(i).JAdd; j<fll.get(i).JAdd+fll.get(i).JLen; j++) { //设置相似但能区分的颜色 mm[j].setBackground(new Color(0xEF5350)); } }*/ }//End Show() //单击(以回收内存) @Override public void mouseClicked(MouseEvent e) { this.MX=e.getX(); this.MY=e.getY(); //System.out.println(this.MX+","+this.MY); //判断在这个框内 if(this.MY<180 && this.MY>80 && this.MX>25 && this.MX<625){ //计算出i值,即落在哪个最小子块里 int i=(this.MX-25)*Main.MAXN/600; //遍历整个满链来查这个i是否是满的位置 for(int j=0;j<fll.size();j++) { Job J=fll.get(j); //如果能找到这个点,说明单击了满链上的一块 if(J.JAdd<=i && i<J.JAdd+J.JLen) { boolean lft=true,rgt=true;//用于记录左右有没有块 //判断左边是不是没有空间或者被满块堵上 if(J.JAdd==0 || (j!=0 && fll.get(j-1).JAdd+fll.get(j-1).JLen==J.JAdd)) lft=false;//记录左边没有空块 //判断右边是不是没有空间或者被满块堵上 if(J.JAdd+J.JLen==Main.MAXN || (j!=fll.size()-1 && fll.get(j+1).JAdd==J.JAdd+J.JLen)) rgt=false;//记录右边没有空块 //回收操作:分4种情况 if(lft==false && rgt==false){ //System.out.println("1"); EPTBlock ep=new EPTBlock(J.JAdd,J.JLen); ept.add(ep);//添加空块 fll.remove(j);//移除满块 } else if(lft==false) { //System.out.println("2"); //遍历空链去查找右边块 for(int r=0;r<ept.size();r++) { if(ept.get(r).EAdd==J.JAdd+J.JLen) { ept.get(r).EAdd=J.JAdd; ept.get(r).ELen+=J.JLen; fll.remove(j);//移除满块 break; } } } else if(rgt==false) { //System.out.println("3"); //遍历空链去查找左边块 for(int r=0;r<ept.size();r++) { if(ept.get(r).EAdd+ept.get(r).ELen==J.JAdd) { ept.get(r).ELen+=J.JLen; fll.remove(j);//移除满块 break; } } } else//双true即都有空块 { //System.out.println("4"); EPTBlock elft=null; EPTBlock ergt=null; //遍历空链去查找右/左块 int r; for(r=0;r<ept.size();r++) { if(ept.get(r).EAdd==J.JAdd+J.JLen)//右块 { ergt=ept.get(r); break;//找到右块后可以直接结束循环了! } else if(ept.get(r).EAdd+ept.get(r).ELen==J.JAdd)//左块 elft=ept.get(r); } elft.ELen+=ergt.ELen+J.JLen; ept.remove(r);//移除右空块 fll.remove(j);//移除满块 } break;//合并完就不用再继续循环了 }//End if能找到这个点 }//End for遍历整个满链来查i }//End if在这个框内 this.Show(); } //按下 @Override public void mousePressed(MouseEvent e) { // TODO Auto-generated method stub } //松开 @Override public void mouseReleased(MouseEvent e) { // TODO Auto-generated method stub } //移入 @Override public void mouseEntered(MouseEvent e) { // TODO Auto-generated method stub } //移出 @Override public void mouseExited(MouseEvent e) { //记录移出 this.MX=-1; this.MY=-1; }}
Job.java
//作业块:有起始地址,长度,是否已进入内存class Job { int JAdd; int JLen; boolean JP;//是否已分配内存 Job(int A,int L){ this.JAdd=A; this.JLen=L; this.JP=false;//初始未分配内存 } @Override public String toString() { return "("+JAdd+","+JLen+")"; }}
EPTBlock.java
//空闲块:有起始地址,长度class EPTBlock { public int EAdd; public int ELen; EPTBlock(int A,int L){ this.EAdd=A; this.ELen=L; } @Override public String toString() { return "("+EAdd+","+ELen+")"; }}
Input.java
import java.awt.Container;import java.awt.event.ActionEvent;import java.awt.event.ActionListener;import java.awt.event.KeyEvent;import java.awt.event.KeyListener;import javax.swing.JButton;import javax.swing.JFrame;import javax.swing.JOptionPane;import javax.swing.JPanel;import javax.swing.JTextField;class Input extends JFrame implements ActionListener{ static int k=0;//输入的值 Container con;//顶层容器 JPanel jp;//中间容器 JTextField tf;//文本框 JButton jb;//按钮 //构造器 Input() { /*关于窗体*/ super("输入1:FF/2:BF/3:WF以选择算法!"); setDefaultCloseOperation(EXIT_ON_CLOSE);//按关闭时:退出 setExtendedState(NORMAL);//扩展状态 setLocation(400,250);//位置 setSize(300,80);//尺寸 /*初始化顶层容器Container和中间容器JPanel*/ con=getContentPane();//顶层容器直接从JFrame获得 jp=new JPanel();//初始化一个中间容器JPanel /*初始化其它容器*/ tf=new JTextField(10);//文本框 jb=new JButton("开始演示");//按钮 /*键盘事件:判断键入的是否为数字*/ tf.addKeyListener(new KeyListener() { @Override public void keyTyped(KeyEvent e) { int temp = e.getKeyChar();//ASCII码 if(temp == 10){//按回车时 } else if(temp != 8)//没有按backspace时 { //下面检查是不是在0~9之间 if(temp>57){ e.consume();//如果不是则消除key事件,也就是按了键盘以后没有反应; }else if(temp<48){ e.consume(); } } } @Override public void keyReleased(KeyEvent e) { // TODO Auto-generated method stub } @Override public void keyPressed(KeyEvent e) { // TODO Auto-generated method stub } }); //为按钮添加动作响应 jb.addActionListener(this); //以下容器嵌套给了JPanel jp.add(tf); jp.add(jb); //最后顶层容器Container里嵌套了这个JPanel con.add(jp); setVisible(true); }//end of构造器 //动作监听并响应 public void actionPerformed(ActionEvent e) { String cmd = e.getActionCommand();// 根据动作命令,来进行分别处理 if (cmd.equals("开始演示"))//单击按钮时 { String s_num=tf.getText();//获取内容 if(s_num.equals(""))//没输入东西 { JOptionPane.showMessageDialog(null, "请输入1~3之间的数字!"); return ; } k=Integer.parseInt(s_num);//转换成数字 //System.out.println(k); if(k<1 || k>3) JOptionPane.showMessageDialog(null, "请输入1~3之间的数字!"); else//输入正确,交给Main来开始另一个JFrame { Main.k=this.k;//把值传给Main类 this.dispose();//本窗口销毁 } } }//end of动作监听}
FF.java
import java.awt.event.ActionEvent;import javax.swing.JOptionPane;//FF首次适应算法(继承TwoList)class FF extends TwoList{ //FF式空闲检查并插入 private boolean EPTCheck(int L){ //遍历整个空闲链 for(EPTBlock e:ept) { if(L<=e.ELen) {//若需要的L不超过这个空闲块的长度 //对满链的操作 Job j=new Job(e.EAdd,L); fll.add(j); //对空闲链的操作 e.EAdd+=L;//分配后起始地址+L e.ELen-=L;//剩余长度减去L //维护空闲链 if(e.ELen==0)//如果已经没有剩余了 ept.remove(e);//将e移除 return true;//返回true表示成功插入 } } return false; } //FF构造器 FF(){ super(); } @Override void Show() { // TODO Auto-generated method stub super.Show(); } //动作监听并响应 @Override public void actionPerformed(ActionEvent e) { String cmd = e.getActionCommand();// 根据动作命令,来进行分别处理 //单击按钮时 if (cmd.equals("尝试插入")) { String s_num=tf.getText();//获取内容 if(s_num.equals(""))//没输入东西 { JOptionPane.showMessageDialog(null, "还没输入块大小呢!"); return ; } int k=Integer.parseInt(s_num);//转换成数字 //System.out.println(k); if(k<1 || k>Main.MAXN) JOptionPane.showMessageDialog(null, "不在1~MAXN内!"); else//输入正确,尝试做FF算法插入 { if(this.EPTCheck(k)==false) JOptionPane.showMessageDialog(null,"不够插"); else this.Show(); } } }//end of动作监听}
BF.java
import java.awt.event.ActionEvent;import java.util.Collections;import java.util.Comparator;import javax.swing.JOptionPane;//BF最佳适应算法(继承TwoList)class BF extends TwoList{ //有关EPTBlock块大小的匿名比较器(从小到大) public static Comparator<EPTBlock> cmpBF = new Comparator<EPTBlock>(){ @Override public int compare(EPTBlock i, EPTBlock j) { return (i.ELen-j.ELen);//这样优先级队列中是块小的先出 } }; //BF式空闲检查并插入 private boolean EPTCheck(int L){ //插入前把空闲链按块从小到大排序 Collections.sort(ept,cmpBF); //遍历整个空闲链 for(EPTBlock e:ept) { if(L<=e.ELen) {//若需要的L不超过这个空闲块的长度 //对满链的操作 Job j=new Job(e.EAdd,L); fll.add(j); //对空闲链的操作 e.EAdd+=L;//分配后起始地址+L e.ELen-=L;//剩余长度减去L //维护空闲链 if(e.ELen==0)//如果已经没有剩余了 ept.remove(e);//将e移除 return true;//返回true表示成功插入 } } return false; } //BF构造器 BF(){ super(); } @Override void Show() { // TODO Auto-generated method stub super.Show(); } //动作监听并响应 @Override public void actionPerformed(ActionEvent e) { String cmd = e.getActionCommand();// 根据动作命令,来进行分别处理 //单击按钮时 if (cmd.equals("尝试插入")) { String s_num=tf.getText();//获取内容 if(s_num.equals(""))//没输入东西 { JOptionPane.showMessageDialog(null, "还没输入块大小呢!"); return ; } int k=Integer.parseInt(s_num);//转换成数字 //System.out.println(k); if(k<1 || k>Main.MAXN) JOptionPane.showMessageDialog(null, "不在1~MAXN内!"); else//输入正确,尝试做BF算法插入 { if(this.EPTCheck(k)==false) JOptionPane.showMessageDialog(null,"不够插"); else this.Show(); } } }//end of动作监听}
WF.java
import java.awt.event.ActionEvent;import java.util.Collections;import java.util.Comparator;import javax.swing.JOptionPane;//WF最坏适应算法(继承TwoList)class WF extends TwoList{ //有关EPTBlock块大小的匿名比较器(从大到小) public static Comparator<EPTBlock> cmpWF = new Comparator<EPTBlock>(){ @Override public int compare(EPTBlock i, EPTBlock j) { return -(i.ELen-j.ELen);//这样优先级队列中是块大的先出 } }; //WF式空闲检查并插入 private boolean EPTCheck(int L){ //插入前把空闲链按块从大到小排序 Collections.sort(ept,cmpWF); //遍历整个空闲链 for(EPTBlock e:ept) { if(L<=e.ELen) {//若需要的L不超过这个空闲块的长度 //对满链的操作 Job j=new Job(e.EAdd,L); fll.add(j); //对空闲链的操作 e.EAdd+=L;//分配后起始地址+L e.ELen-=L;//剩余长度减去L //维护空闲链 if(e.ELen==0)//如果已经没有剩余了 ept.remove(e);//将e移除 return true;//返回true表示成功插入 } } return false; } //WF构造器 WF(){ super(); } @Override void Show() { // TODO Auto-generated method stub super.Show(); } //动作监听并响应 @Override public void actionPerformed(ActionEvent e) { String cmd = e.getActionCommand();// 根据动作命令,来进行分别处理 //单击按钮时 if (cmd.equals("尝试插入")) { String s_num=tf.getText();//获取内容 if(s_num.equals(""))//没输入东西 { JOptionPane.showMessageDialog(null, "还没输入块大小呢!"); return ; } int k=Integer.parseInt(s_num);//转换成数字 //System.out.println(k); if(k<1 || k>Main.MAXN) JOptionPane.showMessageDialog(null, "不在1~MAXN内!"); else//输入正确,尝试做WF算法插入 { if(this.EPTCheck(k)==false) JOptionPane.showMessageDialog(null,"不够插"); else this.Show(); } } }//end of动作监听}
运行结果
输入不在范围内的数会被越界检查拦下:
选择WF最坏适应算法:
这个算法是每次选择最大的空闲块去插入,初始状态:
其中蓝色(钴蓝/湖蓝)的表示空闲块,红色(品红/橘红)的是分配出去的内存块。
为了区分开物理上连续但并没有合并的块,对空闲和忙碌的块都设置了两种颜色,交叉分配颜色。即一块连续的颜色表示一块连续的内存(是空闲或者忙碌的)。
插入大小为4的块,在WF算法下选择了最大的那块空内存块:
再插入大小为4的块,这时还是会去选最大的那块空内存块:
这时再插入大小为4的块,已经没有这么大的块了,内存里满是碎片:
用鼠标单击一块红色(品红/橘红)的内存块,就可以回收这块内存了。回收的时候,如果这块内存的两边有空的内存块,会把它们合并到一起成为一个更大的空块。
如点击最中间的细的忙碌内存块:
可以看到两边的空内存也连起来了。
新增整理内存碎片的功能
整理内存碎片即紧凑,类似于磁盘碎片的整理过程。
//新增的TwoList成员属性JButton clr;//整理磁盘碎片
//还要修改之前文本框和按钮的位置//文本框和按钮tf=new JTextField();tf.setFont(new java.awt.Font("Dialog",1,30));tf.setBounds(120, 200, 100, 50);jb=new JButton("尝试插入");jb.setBounds(280, 200, 100, 50);clr=new JButton("整理碎片");clr.setBounds(400, 200, 100, 50);//为新按钮添加动作响应clr.addActionListener(this);//为新按钮做容器嵌套jp.add(clr);
//为FF/BF/WF的成员方法actionPerformed添加这种可能else if(cmd.equals("整理碎片")) { Job J=new Job(0,0);//为了防止在空闲链为空时整理碎片出错 //对满链的操作 for(int i=0;i<fll.size();i++) { J=fll.get(i); if(i==0) J.JAdd=0; else J.JAdd=fll.get(i-1).JAdd+fll.get(i-1).JLen; } //对空闲链的操作 ept.clear(); System.gc();//立即垃圾回收,强迫症需求 //在空闲链里插入最后的一大块 if(J.JAdd+J.JLen!=Main.MAXN)//仅当内存不满时才插入 ept.add(new EPTBlock(J.JAdd+J.JLen,Main.MAXN-(J.JAdd+J.JLen))); this.Show(); }
整理碎片后:
阅读全文