加入收藏 | 设为首页 | 会员中心 | 我要投稿 安卓应用网 (https://www.0791zz.com/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 编程开发 > Java > 正文

Java编程思想对象的容纳实例详解

发布时间:2020-05-25 06:14:02 所属栏目:Java 来源:互联网
导读:Java提供了容纳对象(或者对象的句柄)的多种方式,接下来我们具体看看都有哪些方式。

Java提供了容纳对象(或者对象的句柄)的多种方式,接下来我们具体看看都有哪些方式。

有两方面的问题将数组与其他集合类型区分开来:效率和类型。对于Java来说,为保存和访问一系列对象(实际是对象的句柄)数组,最有效的方法莫过于数组。数组实际代表一个简单的线性序列,它使得元素的访问速度非常快,但我们却要为这种速度付出代价:创建一个数组对象时,它的大小是固定的,而且不可在那个数组对象的“存在时间”内发生改变。可创建特定大小的一个数组,然后假如用光了存储空间,就再创建一个新数组,将所有句柄从旧数组移到新数组。这属于“矢量”(Vector)类的行为。然而,由于为这种大小的灵活性要付出较大的代价,所以我们认为矢量的效率并没有数组高。

   在Java中,无论使用的是数组还是集合,都会进行范围检查――若超过边界,就会获得一个RuntimeException(运行期违例)错误。几种常见的集合类:Vector(矢量)、Stack(堆栈)以及Hashtable(散列表)。这些类都涉及对对象的处理――好象它们没有特定的类型。换言之,它们将其当作Object类型处理(Object类型是Java中所有类的“根”类)。从某个角度看,这种处理方法是非常合理的:我们仅需构建一个集合,然后任何Java对象都可以进入那个集合(除基本数据类型外――可用Java的基本类型封装类将其作为常数置入集合,或者将其封装到自己的类内,作为可以变化的值使用)。这再一次反映了数组优于常规集合:创建一个数组时,可令其容纳一种特定的类型。

  对象数组容纳的是句柄,而基本数据类型数组容纳的是具体的数值。

1、集合

   为容纳一组对象,最适宜的选择应当是数组。而且假如容纳的是一系列基本数据类型,更是必须采用数组。当我们编写程序时,通常并不能确切地知道最终需要多少个对象。有些时候甚至想用更复杂的方式来保存对象。为解决这个问题,Java提供了四种类型的“集合类”:Vector(矢量)、BitSet(位集)、Stack(堆栈)以及Hashtable(散列表)。与拥有集合功能的其他语言相比,尽管这儿的数量显得相当少,但仍然能用它们解决数量惊人的实际问题。

这些集合类具有形形色色的特征。例如,Stack实现了一个LIFO(先入先出)序列,而Hashtable是一种“关联数组”,允许我们将任何对象关联起来。除此以外,所有Java集合类都能自动改变自身的大小。所以,我们在编程时可使用数量众多的对象,同时不必担心会将集合弄得有多大。

  1.1 缺点:类型未知

使用Java集合的“缺点”是在将对象置入一个集合时丢失了类型信息。之所以会发生这种情况,是由于当初编写集合时,那个集合的程序员根本不知道用户到底想把什么类型置入集合。若指示某个集合只允许特定的类型,会妨碍它成为一个“常规用途”的工具,为用户带来麻烦。为解决这个问题,集合实际容纳的是类型为Object的一些对象的句柄。这种类型当然代表Java中的所有对象,因为它是所有类的根。当然,也要注意这并不包括基本数据类型,因为它们并不是从“任何东西”继承来的。这是一个很好的方案,只是不适用下述场合:

    (1) 将一个对象句柄置入集合时,由于类型信息会被抛弃,所以任何类型的对象都可进入我们的集合――即便特别指示它只能容纳特定类型的对象。举个例子来说,虽然指示它只能容纳猫,但事实上任何人都可以把一条狗扔进来。

    (2) 由于类型信息不复存在,所以集合能肯定的唯一事情就是自己容纳的是指向一个对象的句柄。正式使用它之前,必须对其进行造型,使其具有正确的类型。

2、Stack

Stack有时也可以称为“后入先出”(LIFO)集合。换言之,我们在堆栈里最后“压入”的东西将是以后第一个“弹出”的。和其他所有Java集合一样,我们压入和弹出的都是“对象”,所以必须对自己弹出的东西进行“造型”。
一种很少见的做法是拒绝使用Vector作为一个Stack的基本构成元素,而是从Vector里“继承”一个Stack。这样一来,它就拥有了一个Vector的所有特征及行为,另外加上一些额外的Stack行为。很难判断出设计者到底是明确想这样做,还是属于一种固有的设计。

下面是一个简单的堆栈示例,它能读入数组的每一行,同时将其作为字串压入堆栈。

import java.util.*;
public class Stacks {
 static String[] months = { 
  "January","February","March","April","May","June","July","August","September","October","November","December" };
 public static void main(String[] args) {
  Stack stk = new Stack();
  for(int i = 0; i < months.length; i++)
   stk.push(months[i] + " ");
  System.out.println("stk = " + stk);
  // Treating a stack as a Vector:
  stk.addElement("The last line");
  System.out.println(
   "element 5 = " + stk.elementAt(5));
  System.out.println("popping elements:");
  while(!stk.empty())
   System.out.println(stk.pop());
 }
}

months数组的每一行都通过push()继承进入堆栈,稍后用pop()从堆栈的顶部将其取出。要声明的一点是,Vector操作亦可针对Stack对象进行。这可能是由继承的特质决定的――Stack“属于”一种Vector。因此,能对Vector进行的操作亦可针对Stack进行,例如elementAt()方法。

3、Hashtable

 Vector允许我们用一个数字从一系列对象中作出选择,所以它实际是将数字同对象关联起来了。但假如我们想根据其他标准选择一系列对象呢?堆栈就是这样的一个例子:它的选择标准是“最后压入堆栈的东西”。这种“从一系列对象中选择”的概念亦可叫作一个“映射”、“字典”或者“关联数组”。从概念上讲,它看起来象一个Vector,但却不是通过数字来查找对象,而是用另一个对象来查找它们!这通常都属于一个程序中的重要进程。

在Java中,这个概念具体反映到抽象类Dictionary身上。该类的接口是非常直观的size()告诉我们其中包含了多少元素;isEmpty()判断是否包含了元素(是则为true);put(Object key,Object value)添加一个值(我们希望的东西),并将其同一个键关联起来(想用于搜索它的东西);get(Object key)获得与某个键对应的值;而remove(Object Key)用于从列表中删除“键-值”对。还可以使用枚举技术:keys()产生对键的一个枚举(Enumeration);而elements()产生对所有值的一个枚举。这便是一个Dictionary(字典)的全部。

Dictionary的实现过程并不麻烦。下面列出一种简单的方法,它使用了两个Vector,一个用于容纳键,另一个用来容纳值:

import java.util.*;
public class AssocArray extends Dictionary {
 private Vector keys = new Vector();
 private Vector values = new Vector();
 public int size() { return keys.size(); }
 public boolean isEmpty() {
  return keys.isEmpty();
 }
 public Object put(Object key,Object value) {
  keys.addElement(key);
  values.addElement(value);
  return key;
 }
 public Object get(Object key) {
  int index = keys.indexOf(key);
  // indexOf() Returns -1 if key not found:
  if(index == -1) return null;
  return values.elementAt(index);
 }
 public Object remove(Object key) {
  int index = keys.indexOf(key);
  if(index == -1) return null;
  keys.removeElementAt(index);
  Object returnval = values.elementAt(index);
  values.removeElementAt(index);
  return returnval;
 }
 public Enumeration keys() {
  return keys.elements();
 }
 public Enumeration elements() {
  return values.elements();
 }
 // Test it:
 public static void main(String[] args) {
  AssocArray aa = new AssocArray();
  for(char c = 'a'; c <= 'z'; c++)
   aa.put(String.valueOf(c),String.valueOf(c)
       .toUpperCase());
  char[] ca = { 'a','e','i','o','u' };
  for(int i = 0; i < ca.length; i++)
   System.out.println("Uppercase: " +
       aa.get(String.valueOf(ca[i])));
 }
}

在对AssocArray的定义中,我们注意到的第一个问题是它“扩展”了字典。这意味着AssocArray属于Dictionary的一种类型,所以可对其发出与Dictionary一样的请求。如果想生成自己的Dictionary,而且就在这里进行,那么要做的全部事情只是填充位于Dictionary内的所有方法(而且必须覆盖所有方法,因为它们――除构建器外――都是抽象的)。

Vector key和value通过一个标准索引编号链接起来。也就是说,如果用“roof”的一个键以及“blue”的一个值调用put()――假定我们准备将一个房子的各部分与它们的油漆颜色关联起来,而且AssocArray里已有100个元素,那么“roof”就会有101个键元素,而“blue”有101个值元素。而且要注意一下get(),假如我们作为键传递“roof”,它就会产生与keys.index.Of()的索引编号,然后用那个索引编号生成相关的值矢量内的值。

main()中进行的测试是非常简单的;它只是将小写字符转换成大写字符,这显然可用更有效的方式进行。但它向我们揭示出了AssocArray的强大功能。

标准Java库只包含Dictionary的一个变种,名为Hashtable(散列表,注释③)。Java的散列表具有与AssocArray相同的接口(因为两者都是从Dictionary继承来的)。但有一个方面却反映出了差别:执行效率。若仔细想想必须为一个get()做的事情,就会发现在一个Vector里搜索键的速度要慢得多。但此时用散列表却可以加快不少速度。不必用冗长的线性搜索技术来查找一个键,而是用一个特殊的值,名为“散列码”。散列码可以获取对象中的信息,然后将其转换成那个对象“相对唯一”的整数(int)。所有对象都有一个散列码,而hashCode()是根类Object的一个方法。Hashtable获取对象的hashCode(),然后用它快速查找键。这样可使性能得到大幅度提升(④)。散列表的具体工作原理已超出了本书的范围(⑤)――大家只需要知道散列表是一种快速的“字典”(Dictionary)即可,而字典是一种非常有用的工具。

③:如计划使用RMI(在第15章详述),应注意将远程对象置入散列表时会遇到一个问题(参阅《Core Java》,作者Conrell和Horstmann,Prentice-Hall 1997年出版)

(编辑:安卓应用网)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读