《Java编程思想》—持有对象

今天学习了《Java编程思想》—持有对象这一章,对Java的容器类有了新的理解和巩固。下面先来看看Java容器类图:在这里插入图片描述
可以看到Java容器类库主要可以划分为两个不同的概念:
Collection:一个独立元素的序列,这些元素都服从一条或多条规则。List必须按照插入的顺序保存元素,而Set不能有重复的元素,Queue按照排队顺序来确定对象的产生顺序。
Map:一组成对的“键值对”对象,允许你使用键来查找值。
从图中可以看到只有四种容器:Map、List、Set、Queue。
下面就来学习这四种容器:
首先我们在编写大部分代码时候都是与这些接口在打交道,并且唯一需要指定所使用的精确类型的地方就是在创建的时候,因此,你可以使用下面的方式创建一个List:

List<String> list = new ArrayList<String>();
//甚至
Collection<String> list = new LinkedList<String>();

使用接口的目的在于如果你决定去修改你的实现时,只需要在创建时修改它。但是,这种方式并非总能奏效。由多态性和动态绑定可知,上面代码创建的list只能调用List或Collection接口中的方法,而ArrayList或LinkedList中特有的方法,list是不能调用的。如果你需要这些方法,就不能将他们向上转型为更通过的接口。因此,我们必须清楚的了解各个接口中的方法,先来看看Collection这个总接口:

public interface Collection<E>extends Iterable<E>

在这里插入图片描述
其中addAll方法可以向容器中添加一组元素:

        List<Integer> list1 = new ArrayList<>(Arrays.asList(1,2,3));//可以使用可变长参数列表
        Integer [] integers = {4,5,6};
        ((ArrayList<Integer>) list1).addAll(Arrays.asList(integers));//也可以使用数组
        Collections.addAll(list1,7,8,9);
        Collections.addAll(list1,integers);
        List<Integer> list2 = Arrays.asList(1,2,3,4,5,6,7,8,9,4,5,6); //Arrays.asList()产生的List对象集合在底层是以数组作为实现的
        //list2.add(2);  数组的大小是不能改变的,Runtime error java.lang.UnsupportedOperationException

从上面的代码我们还看到一个Collections.addAll()方法;这个方法位于Collections类中,Collections是一个包装类,包含有各种关于集合操作的静态多态方法。
iterator()方法返回一个迭代器。
removeAll()方法相当于差,retainAll()方法相当于于交。

然后我们来看List接口:
List接口中最关键的一个就是提供了特殊的迭代器,ListIterator,除了允许Iterator接口提供正常的操作外,该迭代器还允许元素插入和替换,以及双向访问。还提供了一个方法来获取从列表中指定位置开始的列表迭代器。
接着是ArrayList和LinkedList:
ArrayList中有一个trimToSize()方法,将ArrayList实例的容量裁剪到该线性表的当前大小。(ArrayList是由数组实现的)
LinkedList是由链表实现的,其中包含有addFirst(),addLast(),getFirst(),getLast(),removeFirst(),removeLast()一系列的方法。

ArrayList和LinkedList还有一个区别就是:标记接口java.util.RandomAccess附到了ArrayList上,而没有附到LinkedList上,实际经验证明,ArrayList在size很大的时候,使用for循环进行访问时要快于使用迭代器iterator,LinkedList则使用迭代器进行访问较好。

再看看Queue接口:
普通队列使用先进先出的方式排序各个元素:
在这里插入图片描述
在这里,往队列中插入、移除和查看(返回队头而不移除)一个元素都有两种方法,一种方法是在队列为空或满时抛出异常,另外一种则是返回null。

Set接口拓展了Collection接口,但是并没有实现新的方法和常量,只是规定了Set中不能含有重复的元素,实际上Set就是Collection。由于HashSet采用HashCode来实现。因此,在HashSet中取出一个特定元素的时间复杂度为O(1),比起List来要快得多,所以当需要从容器中取出一个特定元素的时候,使用hashSet是一个好的选择。LinkedHashSet支持对集合内的元素排序,保持了元素插入时的顺序。
TreeSet可以强加一个不同的顺序,实现了SortedSet接口。

最后就是Map接口:
在这里插入图片描述
Map只能通过键来查询值,只能用get(Object key)方法
entrySet():Set<Map.Entry<K,V>>返回一个包含该映射表中所有条目的集合,注意看Set集合中的每个元素都是一对键值,即一个entry对,这就将容器拓展到了二维,给容器编程带来很大的便利,例如,之前我们学习的数组:

ArrayList<String> [] array = new ArrayList[5];

这就将数组进行了拓展,数组的每一个元素都是一个数组链表。
还比如Map<Person,ArrayList>,从而可以很容易的将容器组合起来从而快速的生成强大的数据结构。
keySet() 和 values 分别返回Map中所有键和值的集合。

最后我们来看看关于迭代器的问题:

public interface Iterator<E>

Iterator接口中有三个方法:
在这里插入图片描述
它还有一个子接口—ListIterator(之前说过的,在List接口实现了这个接口,可以对list进行双向遍历)
在这里插入图片描述
迭代器能够将遍历序列的操作与序列底层的结构相分离,无须知道容器内部的结构是什么。实现一个迭代器,只需要实现Iterator接口,重写其三个方法即可。
使用next()获得序列中的下一个元素。
使用hasNext()检查序列中是否还有元素。
使用remove()将迭代器新近返回的元素删除,即移除由next()产生的最后一个元素,这意味了调用remove()方法之前必须调用next()。但如果你只是向前遍历容器,而不打算修改容器本身,那么使用foreach循环会显得更加简洁。

public class IterableClass implements Iterable<String>{
    protected String [] word = ("And that is how we know the Earth to be banana-shaped").split(" ");

    @Override
    public Iterator<String> iterator() {
        return new Iterator<String>() {
            private int index= word.length-1;
            @Override
            public boolean hasNext() {
                return index >= 0;
            }

            @Override
            public String next() {
                return word[index--];
            }

            public void remove(){
                throw new UnsupportedOperationException();
            }
        };
    }

使用内部类实现了Iterator接口,返回一个迭代器Iterator来进行遍历。上一章的内部类中,提到过可以使用两个内部类来实现同一个接口的不同实现,提供了新的思路。

最后,来看看Foreach与迭代器:
foreach语句主要用于数组,但是他也可以用于任何的Collection对象,之所以能够工作,是因为Java SE5引入了新的被称为Iterable接口(注意,不是Iterator接口,Iterable接口位于java.lang包中,而Iterator位于java.util中),该接口包含一个能够产生Iterator的iterator()方法,并且Iterable接口被foreach用来在序列中移动。因此,如果你创建了任何实现Iterator的类,都可以将他用于foreach语句中。

public class ReversibleArrayList<T> extends ArrayList<T> {
    public ReversibleArrayList(Collection<T> c) {
        super(c);
    }

    public Iterable<T> reversed() {
        return new Iterable<T>() {
            @Override
            public Iterator<T> iterator() {
                return new Iterator<T>() {
                    int current = size() - 1;
                    public boolean hasNext() {return current > -1; }
                    public T next() {return get(current--); }
                    public void remove() { throw new UnsupportedOperationException(); }
                };
            }
        };
    }


    public static void main(String args[]){
        ReversibleArrayList<String> ral = new ReversibleArrayList<>(Arrays.asList("To be or not to be".split(" ")));
        for (String s:ral){
            System.out.print(s + " ");
        }

        System.out.println(" ");

        for (String s:ral.reversed()){
            System.out.print(s + " ");
        }
    }
}/**output:
  *To be or not to be  
  *be to not or be To 
 */

如果直接将ral对象置于foreach语句中,将得到默认的前项迭代器,但是如果调用reversed()方法,就会产生不同的行为。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值