zoukankan      html  css  js  c++  java
  • Java编程思想---第十一章 持有对象(上)

    第十一章 持有对象(上)

      如果一个程序只包含固定数量的且其生命期都是已知的对象,那么这是一个非常简单的程序。为解决数组尺寸固定这一限制,Java实用库提供了一套相当完整的容器类来解决这个问题,其中基本类型是List、Set、Queue、Map,这些对象类型也称为集合类。

     

    11.1 泛型和类型安全的容器

     

      现在,你可以把ArrayList当作一个可以自动扩充自身尺寸的数组来看待,使用ArrayList相当简单,创建一个实例,用add()插入对象,然后用get()访问这些对象,此时需要使用索引,就像数组一样,但是不需要方括号。ArrayList还有一个size()方法,使你知道已经有多少元素添加进来。

      在下面的例子中,Apple和Orange都放置在了容器中,然后将他们取出,正常情况下Java编译器会报告警告信息,因为这个实例没有使用泛型,这里我们使用@SuppreeeWarning注解及其参数表示只有有关不受检查的异常的井盖信息应该被抑制:

    import java.util.ArrayList;
    
    class Apple {
        private static long counter;
        private final long id = counter++;
        public long id() { return id; }
    }
    
    class Orange {}
    
    public class ApplesAndOrangesWithoutGenerics {
        @SuppressWarnings("unchecked")
        public static void main(String[] args) {
            ArrayList apples = new ArrayList();
            for(int i = 0; i<3; i++)
                apples.add(new Apple());
            apples.add(new Orange());
            for(int i = 0; i< apples.size(); i++)
                ((Apple)apples.get(i)).id();
        }
    }

     

      Apple和Orange类是有区别的,他们除了都是Object之外没有任何共性,因为ArrayList保存的是Object,因此你不仅可以通过ArrayList的add()方法将Apple对象放进这个容器,还可以添加Orange对象,而且在编译和运行时都不会有问题。当你使用get()方法取出你认为是Apple对象的时候,你得到的知识Object引用,必须将其转型为Apple,因此就有了以上的表达式。当运行时,当你试图将Orange对象转型为Apple时,你就会以前面提及的异常的形式得到一个错误。

      要想定义用来保存Apple对象的ArrayList,你可以声明ArrayList<Apple>,而不仅仅只是ArrayList:

    import java.util.ArrayList;
    public class ApplesAndOrangesWithGenerics {
        public static void main(String[] args) {
            ArrayList<Apple> apples = new ArrayList<Apple>();
            for(int i = 0; i<3; i++)
                apples.add(new Apple());
            for(int i = 0; i<apples.size(); i++)
                System.out.println(apples.get(i).id());
            for(Apple c : apples)
                System.out.println(c.id());
        }
    }

     

    输出结果为:

    0

    1

    2

    0

    1

    2

     

      现在编译器可以组织你将Orange放置到apples中,因此他变成了一个编译期错误,而不是运行时错误。

      当你指定了某个类型作为泛型参数时,你并不仅限于只能将该确切类型的对象放置到容器中,向上转型也可以像作用于其他类型一样作用于泛型:

    import java.util.ArrayList;
    
    class GrannySmith extends Apple {}
    class Gala extends Apple {}
    class Fuji extends Apple {}
    class Braeburn extends Apple {}
    
    public class GenericsAndUpcasting {
        public static void main(String[] args) {
            ArrayList<Apple> apples = new ArrayList<Apple>();
            apples.add(new GrannySmith());
            apples.add(new Gala());
            apples.add(new Fuji());
            apples.add(new Braeburn());
            for(Apple c : apples)
                System.out.println(c);
        }
    }

     

      因此你可以将Apple的子类添加到指定为保存Apple对象的容器中。

     

    11.2 基本概念

     

      Java容器类类库的用途是保存对象,并将其划分为两个不同的概念:

    1、Collection:一个独立元素的序列,这些元素都服从一条或多条规则,List必须按照插入的顺序保存元素,而Set不能有重复元素,Queue按照排队规则来确定对象产生的顺序。

    2、Map:一组成对的键值对对象,允许使用键值对来查找值,ArrayList允许你使用数字来查找值,因此在某种意义上讲,它将数字与对象关联在了一起,映射表允许我们使用另一个对象来查找某个对象,它也被成为关联数组。

     

    11.3 添加一组元素

     

      在java.util的Arrays和Collections中有很多实用的方法,在一个Collection中添加一组元素,Arrays.asList()方法接收一个数组或是一个用逗号分隔的元素列表,并将其转换为一个List对象,将元素添加到Collection中:

    import java.util.*;
    
    public class AddingGroups {
        public static void main(String[] args){
            Collection<Integer> collection =
                    new ArrayList<Integer>(Arrays.asList(1,2,3,4,5));
            Integer[] moreInts = {6,7,8,9,10};
            collection.addAll(Arrays.asList(moreInts));
            Collections.addAll(collection,11,12,13,14,15);
            Collections.addAll(collection,moreInts);
            List<Integer> list = Arrays.asList(16,17,18,19,20);
            list.set(1,99);
        }
    }

     

      Collection构造器可以接受另一个Collection,用它来将自身初始化,因此你可以使用Arrays.List()来为这个构造器产生输入,但是Collection.addAll()方法运行起来要快得多,而且一个不包含元素的Collection,然后调用Collection.addAll()这种方式更方便,因此它是首选。Collection.addAll()成员方法只能接收另一个Collection对象作为参数,因此它不如Arra.asList()或Collections.addAll()灵活,这两个方式使用的都是可变参数列表。

     

    11.4 容器的打印

     

      你必须使用Arrays.toString()来产生数组的可打印表示,但是打印容器无需任何帮助,下面是一个例子:

    import java.util.*;
    
    public class PrintingContainers {
        static Collection fill(Collection<String> collection) {
            collection.add("rat");
            collection.add("cat");
            collection.add("dog");
            collection.add("dog");
            return collection;
        }
    
        static Map fill(Map<String,String> map) {
            map.put("rat", "Fuzzy");
            map.put("cat", "Rags");
            map.put("dog", "Bosco");
            map.put("dog", "Spot");
            return map;
        }
        public static void main(String[] args){
            System.out.println(new ArrayList<String>());
            System.out.println(new LinkedList<String>());
            System.out.println(new HashSet<String>());
            System.out.println(new TreeSet<String>());
            System.out.println(new LinkedHashSet<String>());
            System.out.println(new HashMap<String,String>());
            System.out.println(new TreeMap<String,String>());
            System.out.println(new LinkedHashMap<String,String>());
        }
    }

     

      这里展示了Java容器类库中的两种主要类型,他们的区别在于每个槽保存的元素个数,Collection在每个槽中只能保存一个元素,此类容器包括:List,它以特定的顺序保存一组元素;Set,元素不能重复;Queue,值允许在容器的一端插入对象,并从另外一端移除对象。Map在每个槽内保存了两个对象,即键和值。

      ArrayList和LinkList都是List类型,从输出可以看出,他们都按照被插入的顺序保存元素,两者的不同之处不仅在于执行某些类型的操作时的性能,而且LinkList包含的操作也多余ArrayList。

      HashSet、TreeList和LinkedHashSet都是Set类型,输出显示在Set中,每个相同的项只有保存一次,但是输出也显示了不同的Set实现的存储元素打方式也不同。

      Map使得你可以用键来查找对象,就像一个简单的数据库,键所关联的对象成为值,使用Map可以像使用数组下标一样,正由于这个所以对于每一个键Map只接受存储一次。

     

    11.5 List

     

      List接口在Collection基础上添加了大量的方法,使得可以在List中间插入和移除元素。

      有两种类型的List:

    1、基本的ArrayList,它长于随机访问元素,但是在List的中间插入和移除元素时比较慢

    2、LinkedList,它通过代价较低的在List中间进行的插入和删除操作,提供了优化的顺序访问,在随机访问方面相对比较慢,但是它的特性集较ArrayList更大。

     

      你可以用contains()方法来确定某个对象是否在列表中,如果想移除一个对象则可以将这个对象的引用传递给remove()方法,可以使用indexOf()老发现该对象在List所处位置的索引编号。当确定一个元素是否属于某个List,发现某个元素的索引,以及从某个List中移除一个元素时,都会用到equals()方法。

     

    11.6 迭代器

     

      任何容器类都必须有某种方式可以插入元素将他们再次取回,毕竟持有事物是容器最基本的工作,对于List,add()是插入元素的方法之一,而get()是取出元素的方法之一。

      如果从更高的角度思考,会发现这有个缺点,要使用容器必须对容器的确切类型编程,初看起来这没什么不好,但是考虑这个情况:如果原本是对着List编码的,但是后来发现如果能把相同的代码应用于Set,将会显得非常方便,这时怎么做?

      迭代器的概念可以用于达成此目的,迭代器是一个对象,它的工作是遍历并选择序列中的对象,而客户端程序员不必知道或关心该序列底层结构,此外,迭代器通常被称为轻量级对象,创建他的代价小,因此经常可以见到对迭代器有些奇怪的限制。例如,Java的Iterator只能单向移动,这个Iterator只能用来

      1、使用方法iterator()要求容器返回一个Iterator,Iterator将准备好返回序列中的第一个元素

      2、使用next()获得序列中的下一个元素

      3、使用hasNext()检查序列中是否还有元素

      4、使用remove()将迭代器新近返回的元素删除

     

      有了Iterator就不必为容器中元素的数量操心了,那是hasNext()和next()关心的事情。如果你只是向前遍历List并不打算修改List对象本身,那么foreach将会显得更加简洁。Iterator还可以移除由next()产生的最后一个元素,这意味着在调用remove()之前必须先调用next()。

     

     

  • 相关阅读:
    c# 4.0新功能--隐式类型
    常用的sql语句记录
    算法学习-回文数判断
    算法学习-两数之和
    RSA加密(前端js)和MD5加密(后台)同时使用实例
    layer父页获取弹出层输入框里面的值
    关于checkbox的全选和反选实例
    jQuery实用小技巧-获取选中的的下拉框和返回头部滑动动画
    jQuery实用小技巧--输入框文字获取和失去焦点
    得到网站下的文件虚拟路径的方法
  • 原文地址:https://www.cnblogs.com/parable/p/11522478.html
Copyright © 2011-2022 走看看