Java编程思想第十一章"持有对象"的11.1~11.4小节的笔记和练习题

        11.1 泛型和类型安全的容器
    在JavaSE5之前的容器,编译器允许我们向容器中放入不正确的类型。诸如,有一个Apple对象的容器,它是一个ArrayList,而我们也可以往它里面放入一个Orange对象(这里的Apple和Orange除了继承Object之外,没有其他共性)。因为ArrayList保存的是Object,所以添加Apple、Orange在编译期和运行期都没问题。但是,当从ArrayList中取出时,取出的是Object对象,这就需要强转为Apple或Orange对象。若是操作失误,将Orange强转为Apple就会得到一个异常:java.lang.ClassCastException。
    通过预定义的泛型可以解决这个问题。比如:要想定义用来保存Apple对象的容器ArrayList,我们可以通过类型参数声明一个ArrayList<Apple>的实例。它指定了这个容器可以保存的类型,这就可以防止在编译期将错误类型的对象添加到容器中(会产生编译期错误)。而且,从容器中取出元素时,也不必再进行类型转换。
    当我们指定了某个类型作为泛型参数时,并不仅限于将确切类型的对象添加到容器中,向上转型也适用于泛型。比如,红富士也是一种苹果(Fuji extends Apple),这样就可以将Fuji的对象添加到ArrayList<Apple>类型的容器实例中。这意味着,我们可以将Apple的子类型添加到被指定为保存Apple对象的容器中。
    练习题1:创建一个新类Gerbil(沙鼠),包含int gerbilNumber属性,在构造器中初始化它。添加一个方法hop(),用以打印沙鼠的号码以及它正在跳跃的信息。创建一个ArrayList,并向其中添加一串Gerbil对象。使用get()遍历List,并且对每个Gerbil调用hop()。
    代码如下:
public class Gerbil {

    private int gerbilNumber;

    public Gerbil(int gerbilNumber) {
        this.gerbilNumber = gerbilNumber;
    }

    public void hop() {
        System.out.println("第" + gerbilNumber + "只沙鼠,正在跳跃");
    }

    public static void main(String[] args) {
        List<Gerbil> gerbils = new ArrayList<Gerbil>();
        for (int i = 1; i <= 5; i++) {
            Gerbil gerbil = new Gerbil(i);
            gerbils.add(gerbil);
        }

        int size = gerbils.size();
        for (int i = 0; i < size; i++) {
            Gerbil gerbil = gerbils.get(i);
            gerbil.hop();
        }
    }
}

        11.2 基本概念
    Java容器类类库被划分为两个不同的概念:
    ①、Collection:一个独立元素的序列(一种存放一组对象的方式),这些元素都服从一条或多条规则。List要按照插入顺序保存元素,Set不能有重复元素。Queue按照排队规则(FIFO)来确定对象产生的顺序。
    ②、Map:一组成对的"键值对"对象,允许我们使用键来查找值。而键可以是其他对象,甚至是null。通过其他对象来查找某个对象,也被称为"关联数组"或"字典"。
    练习题2:修改SimpleCollection.java,使用Set来表示c。
    代码如下:
public class SimpleCollection {

    public static void main(String[] args) {
        Collection<Integer> c = new HashSet<>();
        for (int i = 0; i < 10; i++) {
            c.add(i);
        }

        for (Integer i : c) {
            System.out.print(i + " ");
        }
    }
}

    练习题3:修改innerclasses/Sequence.java,使你可以向其中添加任意数量的元素。
    如下代码:
public class SeqContainer<T> {

    private List<T> container = new ArrayList<T>();

    public void add(T t) {
        container.add(t);
    }

    private class SequenceSelector implements Selector {

        private int i = 0;

        @Override
        public boolean end() {
            return i == container.size();
        }

        @Override
        public T current() {
            return container.get(i);
        }

        @Override
        public void next() {
            if (i < container.size()) {
                i++;
            }
        }
    }

    public Selector getSelector() {
        return new SequenceSelector();
    }

    public Selector reverseSelector() {
        return new Selector() {
            private int i = container.size() - 1;

            @Override
            public void next() {
                if (i >= 0)
                    i--;
            }

            @Override
            public boolean end() {
                return i < 0;
            }

            @Override
            public T current() {
                return container.get(i);
            }
        };
    }

    public static void main(String[] args) {
        SeqContainer<Employee> container = new SeqContainer<>();
        for (int i = 0; i < 10; i++) {
            Employee emp = new Employee(i, "compony" + i);
            container.add(emp);
        }

        Selector selector = container.getSelector();
        while (!selector.end()) {
            System.out.println(selector.current());
            selector.next();
        }

        Selector reverseSelector = container.reverseSelector();
        while (!reverseSelector.end()) {
            System.out.println(reverseSelector.current());
            reverseSelector.next();
        }
    }
}

        11.3 添加一组元素
    书中介绍了几种在容器中添加一组元素的方式。
    方式一:使用Collection的构造器,它可以接受另一个Collection。比如:ArrayList的构造器ArrayList(Collection<? extends E> c)。

    方式二:使用Collection的addAll()方法,它可以接受另一个Collection,其方法签名如下:
    boolean addAll(Collection<? extends E> c)

    方式三:使用Arrays的asList()方法,它可以接受数组或一个用逗号分隔的元素列表(可变参数),其方法签名如下:
    static <T> List<T> asList(T... a):返回一个受指定数组支持的固定大小的列表。
    注:asList()方法返回的ArrayList是Arrays的内部类,而不是java.util.ArrayList。

    方式四:使用Collections的addAll()方法,它可以接受数组或一个用逗号分隔的元素列表(可变参数),其方法签名如下:
    static <T> boolean addAll(Collection<? super T> c, T... elements):将所有指定元素添加到指定 collection 中。

    书中说Arrays.asList()在某些场景下需要告诉编译器产生的List类型的实际的目标类型(显式类型参数说明),我这里试了一下发现JDK1.8已经修复了这个问题,而JDK1.8以下会报错:
class Snow {
}

class Powder extends Snow {
}

class Light extends Powder {
}

class Heavy extends Powder {
}

class Crusty extends Snow {
}

class Slush extends Snow {
}

public class AsListInference {

    public static void main(String[] args) {
        List<Snow> snow = Arrays.asList(new Crusty(), new Slush(), new Powder());
        // 在编译器1.8之下报错:Type mismatch: cannot convert from List<Powder> to List<Snow>,解决方法:参考snow2
        List<Snow> snow1 = Arrays.asList(new Light(), new Heavy());
        Collections.addAll(snow, new Light(), new Heavy());
        List<Snow> snow2 = Arrays.<Snow>asList(new Light(), new Heavy());
    }
}

    备注:Arrays和Collections里有很多工具方法来操作容器,比如二分查找、排序、max()、min()等,需要可以自行查询文档。

        11.4 容器的打印
    使用标准输出(System.out)打印容器的时候,由于容器实现了toString()方法,因此,打印结果可读性很好。但是,打印数组则需要借助Arrays.toString()方法,比如打印一个int数组:Arrays.toString(new int[] { 34, 21, 7, 87, 45 })。
    练习题4:创建一个生成器类,它可以在每次调用其next()方法时,产生你最喜欢的电影的名字。在电影名列表的名字用完之后,循环到该列表的开始处。使用这个生成器来填充数组、ArrayList、LinkedList、HashSet、LinkedHashSet和TreeSet,然后打印每个容器。
    代码如下:
public class MovieGenerator {

    private String[] movies = new String[] { "SS", "DD", "HH", "FF", "XX", "ZZ" };
    private int i = 0;

    public String next() {
        return movies[i++ % movies.length];
    }

    public String[] getMovies() {
        return movies;
    }

    public Collection<String> fill(Collection<String> collection) {
        for (int i = 0; i < 8; i++) {
            collection.add(next());
        }

        return collection;
    }

    public static void main(String[] args) {
        MovieGenerator generator = new MovieGenerator();
        System.out.println(Arrays.toString(generator.getMovies()));
        System.out.println(generator.fill(new ArrayList<String>()));
        System.out.println(generator.fill(new LinkedList<String>()));
        System.out.println(generator.fill(new HashSet<String>()));
        System.out.println(generator.fill(new LinkedHashSet<String>()));
        System.out.println(generator.fill(new TreeSet<String>()));
    }
}

运行结果如下:
[SS, DD, HH, FF, XX, ZZ]
[SS, DD, HH, FF, XX, ZZ, SS, DD]
[HH, FF, XX, ZZ, SS, DD, HH, FF]
[XX, ZZ, SS, DD, HH, FF]
[SS, DD, HH, FF, XX, ZZ]
[DD, FF, HH, SS, XX, ZZ]

    备注:本来想用这些电影名: { "电锯惊魂", "死神来了", "咒怨", "音乐僵尸", "僵尸至尊", "驱魔警察" },但是发现TreeSet输出不是预想的顺序,因此使用 { "SS", "DD", "HH", "FF", "XX", "ZZ" }更明显一些,后续文章再关注TreeSet中文排序。
 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值