内容 |
|
这个作业属于哪个课程 |
https://www.cnblogs.com/nwnu-daizh/ |
这个作业的要求在哪里 |
https://www.cnblogs.com/nwnu-daizh/p/11867214.html |
作业学习目标 |
(1) 掌握Vetor、Stack、Hashtable三个类的用途及常用API; (2) 掌握ArrayList、LinkList两个类的用途及常用API; (3) 了解java集合框架体系组成; (4) 掌握Java GUI中框架创建及属性设置中常用类的API; (5) 了解Java GUI中2D图形绘制常用类的API; |
随笔博文正文内容包括:
第一部分:总结第九章、第十章理论知识(40分)
第九章 集合
9.1 Java中的集合框架
整个集合框架大体上可以分为三种:线性集合类型(List 接口:ArrayList、LinkedList...) 、集合类型(Set 接口:HashSet、TreeSet...)、映射类型(Map 接口:HashMap、TreeMap...)。整个 Java 集合框架大致可以用以下图来表示:
9.1.1 迭代器 Iterator
定义:
public interface Iterator{ /** * 如果这个可迭代对象(集合)中还有下一个元素,那么返回 true,否则返回 false */ boolean hasNext(); /** * 返回可迭代对象(集合)中的下一个元素, * 如果没有下一个元素,方法应该抛出一个 NoSuchElementException 异常 */ E next(); /** * 移除集合中最后一次访问的(最后一次调用 next 方法得到的)元素, * 如果这个方法在第一次调用 next 方法之前调用,或者被连续调用, * 那么方法应该抛出一个 IllegalStateException 异常, * 默认实现是抛出一个 UnsupportedOperationException 异常,即不支持的操作 */ default void remove() { throw new UnsupportedOperationException("remove"); } }
使用迭代器的好处之一是迭代器给我们提供了统一的接口来遍历实现了迭代器接口的类的对象,实现了遍历集合方法的复用,减少我们的代码量。
9.2 集合类及集合类的特点和作用
9.2.1 Collection<接口>
List:有序(元素存入集合的顺序和去除的顺序一致),元素都有索引,允许重复元素。
①子类一Vector:底层的数据结构是可变长度的数组。线程同步,增删和查询都很慢。
②子类二ArrayList:底层的数据结构是可变长度数组。线程不同步,替代了Vector,增删速度不快。查询速度很快(因为在内存中是连续空间)。
③子类三LinkedList:底层的数据结构是链表,线程不同步。增删速度很快。查询速度较慢(因为在内存中需要一个个查询,判断地址来寻找下一元素)。
Set:无序(存入和取出顺序可能不一致),不允许重复元素,必须保证元素的唯一性。
①子类一:HashSet,底层的数据结构是哈希表(散列表)。无序,比数组查询效率高,线程不同步。
根据哈希表冲突的特点,为了保证哈希表中元素的唯一性。
该容器中存储元素所属的类应该复写Object类中的hashCode、equals方法。
HashSet的子类:LinkedhashSet:有序
②TreeSet:底层数据结构是二叉树。可以对Set集合的元素按照指定规则进行排序。线程不同步。
add方法新添加元素必须可以同容器已有的元素进行比较,
所以元素所属类应该事先Comparanle接口的compareTo方法,以完成排序。或者添加Comparator比较器,实现compare方法。
排序的两种方式:
1、元素自身具备比较功能,元素实现Comparable接口,覆盖compareTo方法。
2、建立一个比较器对象,该对象实现Comparator接口,覆盖compare方法,并将该对象作为参数传给TreeSet的构造函数(可以用匿名内部类)
9.2.2 Map
Map接口其特点是:元素是成对出现的,以键和值的形式体现出来,键要保证唯一性
1、子类一Hashtable:底层是哈希表数据结构,线程同步,不允许存储null键和null值。
Hashtable子类Properties:用来存储键值对型的配置文件的信息,可以和IO技术相结合
2、子类二HashMap:底层是哈希表数据结构,线程不同步,允许存储null键和null值,替代了HashTable
3、子类三TreeMap:底层是二叉树结构。线程不同步。可以对map集合中的键进行指定顺序排序。
注意:HashSet和TreeSet底层是使用HashMap和TreeMap实现的,值操作键,就是Set集合。
Collection和Map两个接口对元素操作的区别:
存入元素:
Collection接口下的实现类通过add方法来完成,而Map下是通过put方法来完成。
取出元素:
Collection接口下:List接口有两种方式:1、get(脚标);2、通过Iterator迭代方式获取元素;而Vactor多了一种枚举(Enumeration)的方式。Set接口通过迭代的方式获取元素。
Map接口下:先通地keySet获取键的系列,然后通过该系列使用Iterator迭代方式获取元素值.
9.3 映射
映射是一种特殊的对应关系。
映射就是把两个对象对应起来。
对应的对象叫做象,被对应的对象叫做原象。
Java中有非常好的例子。实现了Map接口的HashMap和TreeMap。前面已经提到过Map类是实现键值对的双向链表,这里就是完成了key和value的映射,当然key要是唯一的才可以。
9.4 视图与包装
Arrays.asList方法:在Arrays类中有一个静态方法--asList方法,这个方法作用是:将普通的Java数组包装成一个List集合。
Collections.nCopies方法:与Arrays.asList方法类似的另一个方法那就是在Collection中的nCopies方法。
不可修改的视图:
Collections还有几个方法,用于产生集合的不可修改视图。这些视图对现有的集合增加了一个运行时的检查。如果发现对集合进行修改的话(这里不仅仅是改变数组的大小,并且包括set之类的方法),就会抛出一个异常,同时这个集合将保持未修改的状态。
可以使用如下8种方法来获得不可修改的视图:
1. Collections.unmodifiableCollection
2. Collections.unmodifiableList
3. Collections.unmodifiableSet
3. Collections.unmodifiableSortedSet
5. Collections.unmodifiableNavigableSet
6. Collections.unmodifiableMap
7. Collections.unmodifiableSortedMap
8. Collections.unmodifiableNavigableMap
每个方法都定义于一个接口。例如,Collections.unmodifiableList方法定义于List接口,与ArrayList、LinkedList或者任何实现了List接口的其他类一起协同工作。
9.5 算法
9.5.1.链表
链表用来存储数据,由一系列的结点组成。这些结点的物理地址不一定是连续的,即可能连续,也可能不连续,但链表里的结点是有序的。一个结点由数据的值和下一个数据的地址组成。一个链表内的数据类型可以是多种多样的。数组也是用来存储数据的,与链表相比,需要初始化时确定长度。一个数组内的数据都是同一类型。在Java中,ArrayList是通过数组实现,而LinkedList则通过链表实现。一个简单的链表类如下:
9.5.2.二叉树
二叉树是n(n>=0)个结点的有序集合。每个结点最多有2个子节点,即左结点和右结点,且左右结点顺序不能更改。
当n=0时,为空二叉树;当n=1时,为只有一个根二叉树。
9.5.3.排序
(1)冒泡排序
重复地走访过要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。时间复杂度 O(n²),为稳定算法。
将数依次进行比较,并将大(或小)的,网后放,如下:
(2)快速排序
通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。
(3)选择排序
每一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,直到全部待排序的数据元素排完。 选择排序是不稳定的排序方法(比如序列[5, 5, 3]第一次就将第一个[5]与[3]交换,导致第一个5挪动到第二个5后面)。
(4)插入排序
每步将一个待排序的记录,按其顺序码大小插入到前面已经排序的字序列的合适位置(从后向前找到合适位置后),直到全部插入排序完为止。
每一个数和它前面的数依次进行比较,因为前面的数的顺序是已经排好的
(5)希尔排序
把记录按下标的一定增量分组,对每组使用直接插入排序算法排序;随着增量逐渐减少,每组包含的关键词越来越多,当增量减至1时,整个文件恰被分成一组,算法便终止。
(6)归并排序
建立在归并操作上的一种有效的排序算法,该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。时间复杂度O(n log n) 。
(6)堆排序
利用堆积树(堆)这种数据结构所设计的一种排序算法,它是选择排序的一种。可以利用数组的特点快速定位指定索引的元素。(暂没理解)
9.5.4.递归、迭代
递归是自己调用自己,直到满足结束递归的条件时结束。迭代是不断的循环,直接循环结束。一般来说,能用迭代就不用递归,递归消耗资源大。
9.5.5.位操作
位操作与逻辑运算符是2种不同的东西,初学之时,自己还经常记不清。位操作有6种,即与(&)、或(|)、异或(^)、取反(~)、左移(<<)、右移(>>)。在这些位操作运算符中,只有取反(~)是弹幕运算符,其他5种都是双目运算符。
9.5.6.概率
9.5.7.排列组合
第十章 图形程序设计
10.1 Swing概述
Swing新一代的图形界面工具。使用 Swing 来开发图形界面比 AWT 更加优秀,因为 Swing 是一种轻量级组件,它采用纯 Java 实现,不再依赖于本地平台的图形界面,所以可以在所有平台上保持相同的运行效果,对跨平台支持比较出色。除此之外,Swing 提供了比 AWT 更多的图形界面组件,因此可以开发出美观的图形界面程序。
Swing 类库结构
Swing 组件都采用 MVC(Model-View-Controller,即模型-视图-控制器)的设计,实现 GUI 组件的显示逻辑和数据逻辑的分离,从而允许程序员自定义 Render 来改变 GUI 组件的显示外观,以提供更多的灵活性。
Swing 围绕 JComponent 组件构建,JComponent 则由 AWT 的容器类扩展而来。Swing 组织结构如图 1 所示。
从图 1 可以看出,Swing 组件除了 AbstmctButton 类之外都以 J 开头。Swing 容器组件直接继承 AWT 类库中的容器组件类,其他大部分组件都是继承 JComponet 组件。组件可以划分为容器组件和非容器组件,容器组件包括 JFmme 和 JDialog。其中 JComponent 定义了非容器类的轻量级组件(JBntton、JPanel、JMenu 等)。
Swing 包
Swing 类库由许多包组成,通过这些包中的类相互协作来完成 GUI 设计。其中,javax.swing 包是 Swing 提供的最大包,它包含将近 100 个类和 25 个接口。几乎所有 Swing 组件都在该包中。表 1 列出了常用的 Swing 包。
包名称 | 描述 |
---|---|
javax.swing | 提供一组“轻量级”组件,尽量让这些组件在所有平台上的工作方式都相同 |
javax.swing.border | 提供围绕 Swing 组件绘制特殊边框的类和接口 |
javax.swing.event | 提供 Swing 组件触发的事件 |
javax.swing.filechooser | 提供 JFileChooser 组件使用的类和接口 |
javax.swing.table | 提供用于处理 javax.swing.JTable 的类和接口 |
javax.swing.text | 提供类 HTMLEditorKit 和创建 HTML 文本编辑器的支持类 |
javax.swing.tree | 提供处理 javax.swingJTree 的类和接口 |
javax.swing.event 包中定义了事件和事件监听器类,javax.swing.event 包与 AWT 的 event 包类似。Java.awt.event 和 javax.swing.event 都包含事件类和监听器接口,它们分别响应由 AWT 组件和 Swing 组件触发的事件。
例如,当在树组件中需要节点扩展(或折叠)的通知时,则要实现 Swing 的 TreeExpansionListener 接口,并把一个 TreeExpansionEvent 实例传送给 TreeExpansionListener 接口中定义的方法,而 TreeExpansionListener 和 TreeExpansionEvent 都是在 swing.event 包中定义的。
虽然 Swing 的表格组件(JTable)在 javax.swing 包中,但它的支持类却在 javax.swing.table 包中。表格模型、图形绘制类和编辑器等也都在 javax.swing.table 包中。
与 JTable 类一样,Swing 中的树 JTree(用于按层次组织数据的结构组件)也在 javax.swing 包中,而它的支持类却在 javax.swing.tree 包中。javax.swing.tree 包提供树模型、树节点、树单元编辑类和树绘制类等支持类。
Swing 容器
创建图形用户界面程序的第一步是创建一个容器类以容纳其他组件,常见的窗口就是一种容器。容器本身也是一种组件,它的作用就是用来组织、管理和显示其他组件。
Swing 中容器可以分为两类:顶层容器和中间容器。
顶层容器是进行图形编程的基础,一切图形化的东西都必须包括在顶层容器中。顶层容器是任何图形界面程序都要涉及的主窗口,是显示并承载组件的容器组件。在 Swing 中有三种可以使用的顶层容器,分别是 JFrame、JDialog 和 JApplet。
- JFrame:用于框架窗口的类,此窗口带有边框、标题、关闭和最小化窗口的图标。带 GUI 的应用程序至少使用一个框架窗口。
- JDialog:用于对话框的类。
- JApplet:用于使用 Swing 组件的 Java Applet 类。
中间容器是容器组件的一种,也可以承载其他组件,但中间容器不能独立显示,必须依附于其他的顶层容器。常见的中间容器有 JPanel、JScrollPane、JTabbedPane 和 JToolBar。
- JPanel:表示一个普通面板,是最灵活、最常用的中间容器。
- JScrollPane:与 JPanel 类似,但它可在大的组件或可扩展组件周围提供滚动条。
- JTabbedPane:表示选项卡面板,可以包含多个组件,但一次只显示一个组件,用户可在组件之间方便地切换。
- JToolBar:表示工具栏,按行或列排列一组组件(通常是按钮)。
在 Java 程序中容器类都是继承自 Container 类。中间容器和顶层容器在,AWT 包和 Swing 包中继承 Container 类的继承关系,如图所示。
10.2 创建框架
创建框架:
在Java中,顶层窗口(就是没有包含在任何其他窗口中的窗口)被称为框架(frame)。
在AWT库中有一个称为Frame的类,用于描述顶层窗口。这个类的Swing版本名为JFrame,它扩展于Frame类。
JFrame是极少数几个不绘制在画布上的Swing组件之一。因此,它的修饰部件(按钮、标题栏、图标等)由用户的窗口系统绘制,而不是由Swing绘制。
注意:绝大多数Swing组件类都以“J”开头,例如,JButton,JFrame等。在Java中有Button和Frame这样的类,但它们属于AWT组件。如果偶尔忘记了“J”程序仍然可以编译和运行,但是将Swing和AWT组件混合在一起使用将会导致视觉和行为的不一致。
顶层窗口(没有包含在其他窗口中的窗口)被称为框架,在AWT中用Frame类描述。这个类的Swing版本称为JFrame。JFrame扩展于Frame类,是极少数几个不绘制在画布上的Swing组件之一。
10.3 框架定位
1、框架定位:
setLocation和setBounds方法用于设置框架的位置。
setIconImage用于告诉窗口系统在标题栏、任务切换窗口等位置显示哪个图标。
setTitle:改变标题栏文字
setResizable:利用一个boolean值确定框架的大小是否允许用户改变。
2、JFrame类本身只包含若干个改变外观的方法。然而,继承了各个超类中许多处理框架大小和位置的方法。如:
dispose方法用于关闭窗口,并回收创建窗口所使用的全部系统资源。
setIconImage方法用于将窗口极小化时的图标设置为Image对象。
setTitle方法用于改变标题栏中的文本。
setResizable方法利用一个boolean值确定框架的大小是否允许用户改变。
setLocation(x,y)方法将窗口放置在坐标为x,y的位置。
setBounds(x,y,width,height)方法将窗口指定位置,同时设定窗口大小。
但是对于不同分辨率的屏幕,框架的大小不同,所以应先检查用户屏幕的分辨率,并根据其分辨率编写代码重置框架的大小。这就需要用到Toolkit类,它包含了很多与本地窗口系统进行交付的方法。先调用静态方法getDefaultToolkit得到一个Toolkit对象。然后调用getScreenSize方法,该方法以Dimension对象的形式返回屏幕的大小。Dimension对象用公有实例变量width和height保存屏幕的宽度和高度。
Toolkit kit = Toolkit.getDefaultToolkit();
Dimension screenSize = kit.getScreenSize();
int screenWidth = screenSize.width;
int screenHeight = screenSize.height;
还可以利用这个工具箱加载图像,然后,将这个图像设置为框架的图标
Image img = kit.getImage("icon.gif");
setIconImage(img);
我们经常需要将程序的主框架设置为最大尺寸:
frame.setExtendState(Frame.MAXIMIZED_BOTH);
10.4 处理2D图形
Graphics类包含绘制直线,矩形或者椭圆的方法。但是绘制图形的能力有限,不能改变线的粗细,不能旋转这些图形
Java se 引入了Java 2D库,这个库实现了功能强大的图形操作。
要想使用Java 2D库绘制图形,需要获得一个Graphics2D类对象。这个类是Graphics的子类。自从JavaSE 2版本以来,paintComponent方法就会自动的获得一个Graphics2D类对象,我们只需要进行一次类型转换就可以了
public void paintComponent(Graphics g )
{
Graphics2D g2=(Graphics2D ) g;
}
java 2D库采用面向对象的方式将几何图形组织起来,包含直线、矩形椭圆类:
Line2D
Rectangle2D
Ellipse2D
Java 2D 支持更加复杂的图形,如圆弧,二次曲线,三次曲线和通用路径。
要想绘制图形,首先要创建一个实现了Shape接口的类的对象。然后调用Graphics2D类中的draw方法。
10.5 使用颜色、字体及图像
10.5.1 使用颜色
Graphics2D类的setPaint方法可以为图形环境上的所有后续绘制操作选择颜色。
Color类用于定义颜色,在java.awt.Color中提供了13个预定义的常量,分别表示13种标准颜色。(JDK 1.4之前的版本颜色名字是用小写,后来采用大写,为了兼容,就大小写都保留了。)
例如:
g2.setPaint(Color.RED);
//绘制操作
可以通过提供RGB三色成分来创建一个Color对象,三种颜色成分都是用0~255之间的整型数值表示。
例如:
g2.setPaint(new Color(0,128,128));
//绘制操作
如果使用Graphics对象,而不是Graphics2D对象,就要使用setColor方法设置颜色。
要想设置背景颜色,使用Component类中的setBackground方法。
setForeground方法用来设定在组件上进行绘制时使用的默认颜色。
Color类中的brighter()方法和darker()方法可以分别加亮或者变暗当前的颜色。连续调用可以使效果增强。如:c.brighter().brighter()
SystemColor类中预定义了很多颜色的名字,在这个类中的常量,封装了用户系统的各个元素的颜色。
如SystemColor.window为用户桌面上所有窗口使用的默认颜色。当希望让绘制的用户界面元素与用户桌面上已经存在的其他元素颜色匹配时,使用SystemColor类中的颜色非常有用。
用颜色填充图形,只需要将draw替换为fill就行了。
如:
Rectangle2D rect=…;
g2.setPaint(Color.RED);
g2.fill(rect);
10.5.2 字体的设置
10.5.2.1 计算机上的字体
要想知道某台特定计算机上所允许使用的字体,就需要调用GraphicsEnvironment类中的getAvailableFontFamilyNames方法。这个方法将返回一个字符型数组,其中包含了所有可用的字体名。
GraphicsEnvironment类描述了用户系统的图形环境,为了得到这个类的对象,需要调用静态的getLocalGraphicsEnvironment方法。
下面这个程序将打印出系统上的所有字体名:
10.5.2.2 逻辑字体
为了创建一个公共基准,AWT定义了五个逻辑字体名:
SansSerif
Serif
Monospaced
Dialog
DialogInput
这些字体将被映射到客户机上的实际字体。例如,在Windows系统中,SansSerif将被映射到Arial上。
字体映射定义在Java安装的jre/lib子目录中的fontconfig.properties文件中。
在Java以前的版本中,将Helvetica、TimesRoman、Courier、ZapfDingbats作为逻辑字体名,为了向后兼容,现在仍然将这些字体名按照逻辑字体名对待。
10.5.2.3 使用字体
要想使用某种字体绘制字符,必须首先使用字体名、字体风格、字体大小来创建一个Font类对象,例如:
Font helvb14=new Font(“Helvetica”, Font.BOLD, 14);
第二个参数可以指定字体的风格:常规、加粗、斜体或加粗斜体。
Font.PLAIN、Font.BOLD、Font.ITALIC、Font.BOLD+Font.ITALIC
setFont函数为图形环境选择一种字体。这种字体将被应用于后续的文本绘制操作中。
下面这段代码将使用系统上14号加粗的标准sans serif字体显示字符串“Hello World”:
Font sansbold14= new Font("SansSerif",Font.BOLD,14);
g2.setFont(sansbold14);
String message = "Hello World!";
g2.drawString(message,75,100);
10字体测量.5.2.4
需要精确设定字符串绘制位置时,需要知道字符串占据的宽和高的像素数量,这两个值取决于下面三个因素:
1.使用的字体(如前面所提到的14号加粗的标准sans serif字体)。
2.字符串(如"Hello World!")。
3.绘制字体的设备(如用户屏幕)。
要想得到屏幕设备字体属性的描述对象,需要调用Graphics2D类中的getFontRenderContext方法。它返回一个FontRenderContext类对象。可以直接将这个对象传递给Font类的getStringBounds方法:
FontRenderContext context = g2.getFontRenderContext();
Rectangle2D bounds = f.getStringBounds(message, context);
getStringBounds方法将返回包围字符串的矩形。
getStringBounds方法返回的矩形宽度是字符串水平方向的宽度。矩形的高度是上坡度、下坡度、行间距的总和。该矩形始于字符串的基线,矩形顶部的y坐标为负值。
可以采用下面的方法获得字符串的宽度、高度和上坡度:
double stringWidth = bounds.getWidth();
double stringHeight = bounds.getHeight();
double ascent = -bounds.getY();
如果需要知道下坡度或行间距,可以使用Font类的getLineMetrics方法。这个方法将返回一个LineMetrics类对象,获得下坡度和行间距的方法是:
LineMetrics metrics = f.getLineMetrics(message,context);
float descent = metrics.getDescent();
float leading = metrics.getLeading();
10.5.3 图像
10.5.3.1读入图像
如果图像存储在本地文件中,就应该调用:
Image image = ImageIO.read(new File(filename));(带扩展名)
如果图像存在英特网的某个位置上,提供URL:
Image image = ImageIO.read(new URL(urlname));
如果图像不可用,read方法将抛出一个IOException。
10.5.3.2 显示图像
可以使用Graphics类的drawImage方法将图像显示出来:
注意:这个调用可能会在图像还没有绘制完毕就返回。
boolean drawImage(Image img, int x, int y, ImageObserver observer);
绘制一幅非比例图像。
boolean drawImage(Image img, int x, int y, int width, int height, ImageObserver observer);
绘制一幅比例图像。系统按照比例将图像放入给定宽和高的区域。
xy是图像左上角的坐标,observer是绘制进程中以通告为目的的对象(可能为null) v。
10.6 组件
10.6.1组件类的常用API
java.awt.Component 1.0
boolean isVisible() 检查组件是否可见
void setVisible(boolean b) 设置组件可见
void setSize(int width,int height) 把组件缩放到指定宽度和高度
void setBounds(int x,int y,int width,int height) 移动并缩放组件
Dimension getSize() 得到组件的大小
void setSize(Dimension d) 把组件缩放到指定的大小
void setLocation(int x,int y)
10.6.2 AWT组件
10.6.2 Swing组件
第二部分:实验部分
实验1: 导入第9章示例程序,测试程序并进行代码注释。
测试程序1:
l 使用JDK命令运行编辑、运行以下三个示例程序,结合运行结果理解程序;
l 掌握Vetor、Stack、Hashtable三个类的用途及常用API。
//示例程序1 import java.util.Vector; class Cat { private int catNumber; Cat(int i) { catNumber = i; } void print() { System.out.println("Cat #" + catNumber); } } public class Cats{ public static void main(String[] args){ Vector for(int i=0; i<7; i++) cats.addElement(new Cat(i)); for(int i=0; i (cats.elementAt(i)).print(); } } |
//示例程序2 import java.util.*; public class Stacks { static String[] months={"金","银","铜","铁"}; public static void main(String[] args){ Stack for(int i=0; i stk.push(months[i]); System.out.println(stk); System.out.println("element 2=" + stk.elementAt(2)); while(!stk.empty()) System.out.println(stk.pop()); } } |
//示例程序3 import java.util.*; class Counter { int i = 1; public String toString() { return Integer.toString(i); } }
public class Statistics { public static void main(String[] args) { Hashtable ht = new Hashtable(); for (int i = 0; i < 10000; i++) { Integer r = new Integer((int) (Math.random() * 20)); if(ht.containsKey(r)) ((Counter)ht.get(r)).i++; else ht.put(r, new Counter()); } System.out.println(ht); } } |
示例一运行截图如下:
示例2运行截图如下:
示例3运行截图如下:
总结:
1、Vector 类
Vector 非常类似于 ArrayList,区别是 Vector 是线程同步的。由 Vector 创建的 Iterator,虽然和 ArrayList 创建的 Iterator 是同一接口,但是,因为 Vector 是同步的,当一个 Iterator 被创建而且正在被使用,另一个线程改变了 Vector 的状态(例如,添加或删除了一些元素),这时调用 Iterator 的方法时将抛出 ConcurrentModificationException,因此必须捕获该异常。
2、Stack 类
Stack 继承自 Vector,实现了一个后进先出的堆栈。Stack 提供 5 个额外的方法使得 Vector 得以被当作堆栈使用。除了基本的 Push 和 Pop 方法,还有 Peek 方法得到栈顶的元素,Empty 方法测试堆栈是否为空,Search 方法检测一个元素在堆栈中的位置。注意,Stack 刚创建后是空栈。
3、Hashtable 类
Hashtable 继承 Map 接口,实现了一个基于 Key-Value 映射的哈希表。任何非空(non-null)的对象都可作为 Key 或者 Value。添加数据使用 Put(Key,Value),取出数据使用 Get(Key),这两个基本操作的时间开销为常数。
测试程序2:
l 使用JDK命令编辑运行ArrayListDemo和LinkedListDemo两个程序,结合程序运行结果理解程序;
import java.util.*;
public class ArrayListDemo { public static void main(String[] argv) { ArrayList al = new ArrayList(); // Add lots of elements to the ArrayList... al.add(new Integer(11)); al.add(new Integer(12)); al.add(new Integer(13)); al.add(new String("hello")); // First print them out using a for loop. System.out.println("Retrieving by index:"); for (int i = 0; i < al.size(); i++) { System.out.println("Element " + i + " = " + al.get(i)); } } } |
import java.util.*; public class LinkedListDemo { public static void main(String[] argv) { LinkedList l = new LinkedList(); l.add(new Object()); l.add("Hello"); l.add("zhangsan"); ListIterator li = l.listIterator(0); while (li.hasNext()) System.out.println(li.next()); if (l.indexOf("Hello") < 0) System.err.println("Lookup does not work"); else System.err.println("Lookup works"); } } |
l 在Elipse环境下编辑运行调试教材360页程序9-1,结合程序运行结果理解程序;
l 掌握ArrayList、LinkList两个类的用途及常用API。
ArrayListDemo运行截图如下:
LinkedListDemo程序运行截图如下:
例题9.1程序代码如下:
package linkedList; import java.util.*; /** * This program demonstrates operations on linked lists.//此程序演示对链接列表的操作 * @version 1.12 2018-04-10 * @author Cay Horstmann */ public class LinkedListTest { public static void main(String[] args) { var a = new LinkedList();//创建一个链表对象 a.add("Amy"); a.add("Carl"); a.add("Erica"); var b = new LinkedList (); b.add("Bob"); b.add("Doug"); b.add("Frances"); b.add("Gloria"); //把b中的单词合并成a // merge the words from b into a ListIterator aIter = a.listIterator(); Iterator bIter = b.iterator(); while (bIter.hasNext()) { if (aIter.hasNext()) aIter.next(); aIter.add(bIter.next()); } System.out.println(a); // remove every second word from b //b中每间隔一个元素删除 bIter = b.iterator(); while (bIter.hasNext()) { bIter.next(); // skip one element跳过一个元素 if (bIter.hasNext()) { bIter.next(); // skip next element bIter.remove(); // remove that element } } System.out.println(b); // bulk operation: remove all words in b from a //批量操作:从a中删除b中的所有单词 a.removeAll(b); System.out.println(a); } }
运行截如下:
总结:
1、ArrayList是实现了基于动态数组的数据结构,而LinkedList是基于链表的数据结构;
2、对于随机访问元素,Array获取数据的时间复杂度是O(1),但是要删除数据却是开销很大的,因为这需要重排数组中的所有数据。ArrayList想要get(int index)元素时,直接返回index位置上的元素,而LinkedList需要通过for循环进行查找,虽然LinkedList已经在查找方法上做了优化,比如index < size / 2,则从左边开始查找,反之从右边开始查找,但是还是比ArrayList要慢。
3、对于添加和删除操作add和remove,LinkedList是更快的。因为LinkedList不像ArrayList一样,不需要改变数组的大小,也不需要在数组装满的时候要将所有的数据重新装入一个新的数组,这是ArrayList最坏的一种情况,时间复杂度是O(n),而LinkedList中插入或删除的时间复杂度仅为O(1)。ArrayList在插入数据时还需要更新索引(除了插入数组的尾部)。 ArrayList想要在指定位置插入或删除元素时,主要耗时的是System.arraycopy动作,会移动index后面所有的元素;LinkedList主耗时的是要先通过for循环找到index,然后直接插入或删除。这就导致了两者并非一定谁快谁慢。
4、链表是一个有序集合(ordered collection),每个对象的位置十分重要。LinkedList.add方法是继承于Collection.add方法,它们都是将对象添加到链表的尾部,但是我们使用链表数据结构是为了快速地在指定位置进行添加或删除操作,由于迭代器是描述集合中位置的,所以这种依赖于位置的add方法将由迭代器负责。Java集合类库便提供了子接口ListIterator。
与Collection.add方法不同,ListIterator.add方法不返回boolean类型值,它假定添加操作总会改变链表;
另外,ListIterator接口还有两个方法,可以用来反向遍历链表。
实验2:导入第10章示例程序,测试程序并进行代码注释。
测试程序1:
l 运行下列程序,观察程序运行结果。
import javax.swing.*; public class SimpleFrameTest { public static void main(String[] args) { JFrame frame = new JFrame(); frame.setBounds(0, 0,300, 200); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setVisible(true); } } |
l 在elipse IDE中调试运行教材407页程序10-1,结合程序运行结果理解程序;与上面程序对比,思考异同;
l 掌握空框架创建方法;
l 了解主线程与事件分派线程概念;
掌握GUI顶层窗口创建技术。
测试程序1运行截图如下:
例题10.1程序代码如下:
package simpleFrame; import java.awt.*; import javax.swing.*; /** * @version 1.34 2018-04-10 * @author Cay Horstmann */ public class SimpleFrameTest { public static void main(String[] args) { EventQueue.invokeLater(() -> { var frame = new SimpleFrame();//定义了一个子类SimpleFrame frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//定义一个用户关闭框架时的响应动作 frame.setVisible(true);//为了显示框架。main方法调用了框架的setVisble方法 }); } } class SimpleFrame extends JFrame { private static final int DEFAULT_WIDTH = 300; private static final int DEFAULT_HEIGHT = 200;//将构造器框架设置为300×200像素 public SimpleFrame() { setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT); } }
运行截图如下:
两程序的区别:当我们需要在一个窗体中加入一个按钮组件时,基于AWT实现与基于Swing实现是不一样的。而且AWT中是Frame,Swing中是JFrame。导入的包也不同,前者只需导入import java.awt.*;即可,而后者需要导入import java.awt.*;import以及javax.swing.*;包等。
测试程序2:
l 在elipse IDE中调试运行教材412页程序10-2,结合运行结果理解程序;
掌握确定框架常用属性的设置方法。
例题10.2程序代码如下:
package sizedFrame; import java.awt.*; import javax.swing.*; /** * @version 1.35 2018-04-10 * @author Cay Horstmann */ public class SizedFrameTest { public static void main(String[] args) { EventQueue.invokeLater(() -> { var frame = new SizedFrame();//定义了一个子类SimpleFrame frame.setTitle("SizedFrame");////定义一个用户关闭框架时的响应动作 frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setVisible(true);//为了显示框架。main方法调用了框架的setVisble方法 }); } } class SizedFrame extends JFrame { public SizedFrame() { // get screen dimensions获取屏幕尺寸 Toolkit kit = Toolkit.getDefaultToolkit();//调用一个Toolkit的静态方法getDefauitToolkit得到一个Toolkit对象 Dimension screenSize = kit.getScreenSize();//返回屏幕尺寸大小 int screenHeight = screenSize.height; int screenWidth = screenSize.width; // set frame width, height and let platform pick screen location setSize(screenWidth / 2, screenHeight / 2); setLocationByPlatform(true);//将窗口大小设置为取值的一半,告知窗口系统定位框架 // set frame icon//设置新图标 Image img = new ImageIcon("icon.gif").getImage(); setIconImage(img); } }
运行截图如下:
测试程序3:
l 在elipse IDE中调试运行教材418页程序10-3,结合运行结果理解程序;
l 掌握在框架中添加组件;
掌握自定义组件的用法。
例题10.3程序代码如下:
运行截图如下:
实验总结:
1):通过本次课程的学习让我们对Java中第九章中的集合与第十章中的图像程序设计程序的相关内容有了初步的了解;在上课老师的讲解过程中学习了Java中AWT和Swing的简介及框架构造的等知识。了解了使用用AWT与Swing编写运行程序的不同与相同;了解java集合框架体系组成等相关内容。
2):在实验课和课下的学习过程中掌握Vetor、Stack、Hashtable三个类的用途及常用API;及ArrayList、Lin了解Java GUI中2D图形绘制常用类的API;kList两个类的用途及常用API;在实验添加注释的过程中对上课时的知识点有了进一步的了解。
3):通过这次的实验使我学到了不少实用的知识,并且锻炼了自己的动手能力;虽然在学习的过程中遇到了许多不懂得地方,但是通过问同学及查找相关资料都一一解决了;相信在以后的学习中,通过不断努力,我可以学到更多的关于Java编程的相关知识。