容器(一):容器双雄之Collection

容器(一):容器双雄之Collection

标签: Java编程思想


容器双雄分别是谁?

在很多讲容器的博客里,开头放一张“容器家族”图好像是惯例一样。

所以我就没放图。

从作用上讲:引入容器类库的用途是“保存对象”。

从划分上将:分为Collection接口和Map接口两大体系。
这两个体系最根本的区别是:Collection是一个独立元素的序列;Map是一组成对的“键值对”对象。

  • Collection划分为:List,Set,Queue

    • List接口常见有:ArrayList,LinkedList,AbstractList, CopyOnWriteArrayList, Vector,Stack
    • Set接口常见有:HashSet,TreeSet,LinkedHashSet,AbstractSet,CopyOnWriteArraySet,EnumSet
    • Queue接口常见有:(阻塞队列)ArrayBlockQueue、PriorityBlockingQueue、LinkedBlockingQueue(双端队列)ArrayDeque、LinkedBlockingDeque、LinkedList
  • Map一般使用:Hashmap、Treemap、Hashtable、AbstractMap、ConcurrentHashMap、 LinkedHashMap、WeakHashMap

以上的分类包括了concurrent包的集合类,接下来concurrent包的集合类不在讨论范围之内。

Collection个人简历

Collection接口是最基本的集合接口,也是Collection层次结构中的根接口。这也就是成为“双雄”之一的原因。

Collection系列的特点是“守规矩”。是一个独立元素的序列,这些元素都必须遵循一条或者多条规则。比如:List必须按照插入的顺序保存元素;Set不能有重复的元素;Queue按照队列法则来确定顺序……
重复的、不重复的、有序的、无序的,总之Collection系列在遵循着自己的“规矩”。

Collection接口,作为 容器类的双雄之一,Collection系列的教父,是描述了其子类的共性的一个接口。Collection不提供任何直接的实现方法,都是通过其子接口来实现的。但是还有一种说法是:Collection接口是为了要表示其他若干个的接口的共性而出现的“附属接口”。另外,java.util.AbstractCollection类提供了Collection方法的默认实现,因此我们可以创建AbstractCollection的子类型,而其中没有不必要的代码重复。

在Java中所有实现了Collection接口的类都必须提供两套标准的构造函数,一个是无参,用于创建一个空的Collection,一个是带有Collection参数的有参构造函数,用于创建一个新的Collection,这个新的Collection与传入进来的Collection具备相同的元素。

Collection接口的方法

Collection提供了一些基本的方法,但是没有实现它们,下面通过代码来直观的了解它们:

public static void TestCollectionMethod1() {
        Collection collection1 = new ArrayList();

        //size():返回集合中对象的个数
        System.out.println(collection1.size());

        //add(Object obj):向集合中添加一个对象
        collection1.add(123);    //Integer类型
        collection1.add("AA");
        collection1.add(new Date());
        collection1.add("BB");
        System.out.println(collection1.size());

        //addAll(Collection c):将形参c中包含的所有对象添加到当前集合中
        Collection collection2 = Arrays.asList(1,2,3);
        collection1.addAll(collection2);
        System.out.println(collection1.size());

        //查看集合对象(ArrayList重写的toString方法)
        System.out.println(collection1);

        //isEmpty();判断集合是否为空
        System.out.println(collection1.isEmpty());

        //clear();清空集合中的对象
        collection1.clear();
        System.out.println(collection1.isEmpty());
    }

    public static void TestCollectionMethod2() {
        Collection collection = new ArrayList();
        collection.add(123);
        collection.add("AA");
        collection.add(new Date());
        collection.add("BB");
        Person p1 = new Person("JJ",23);
        collection.add(p1);

        System.out.println(collection.size());

        //contains(Object obj):判断集合中是否包含指定的obj元素。包含true,反之false
        boolean b1 = collection.contains(123);
        b1 = collection.contains("AA");
        System.out.println(b1);

        boolean b2 = collection.contains(p1);
        System.out.println(b2);

        //属性一样但是是两个对象,因此地址值不一样,要想返回true,得重写Person中的equals
        //根据对象所在的类的equals方法进行判断,是否重写了equals方法
        collection.add(new Person("ZZ",23));
        boolean b3 = collection.contains(new Person("ZZ",23));
        System.out.println(b3);

        //containsAll(Collection c):判断当前集合中是否包含c中的所有元素
        Collection collection1 = new ArrayList();
        collection1.add(123);
        collection1.add("AA");
        boolean b4 = collection.containsAll(collection1);
        System.out.println("$" + b3);

        //retainAll(Collection c):求当前集合与c的共有对象,返回给当前集合
        collection.retainAll(collection1);
        System.out.println(collection);

        //remove(Object obj):删除本集合中的obj元素,删除成功返回true 否则false
        boolean b5 = collection.remove("BB");
        System.out.println(b5);

        //removeAll(Collection c):从当前集合中删除包含在c中的元素

        //equals(Object obj):判断集合中的所有元素是否完全相同

        //toArray():将集合转化为数组
        Object[] obj = collection.toArray();
        for(Object o : obj) {
            System.out.println(o);
        }

        //iterator():返回一个Iterator实现类的对象,实现集合的遍历
        Iterator iterator = collection.iterator();
        System.out.println();
        System.out.println(iterator.next());
        System.out.println(iterator.next());

        while(iterator.hasNext()) {
            System.out.println(iterator.next());
        }
    }

Iterator与foreach

Iterator

遍历是我们在数组中常用的操作,那么在容器中,Java专门提供了遍历容器中所有对象的方法:迭代器。迭代其实我们可以简单地理解为遍历,是一个标准化遍历各类容器里面的所有对象的方法类。

Collection接口中实现了iterator()方法,返回在此 collection 的元素上进行迭代的迭代器。

迭代器的终极目标:就是用统一的方法来迭代不同类型的集合!
可能由于不同集合的内部数据结构不尽相同,如果要自己纯手工迭代的话相互之间会有很大的差别,而迭代器的作用就是统一的方法对不同的集合进行迭代,而在迭代器底层隐藏不同集合之间的差异,从而为迭代提供最大的方便。

迭代器是一种模式,它可以使得对于序列类型的数据结构的遍历行为与被遍历的对象分离,从而避免向客户端暴露集合的内部结构。即我们无需关心该序列的底层结构是什么样子的。只要拿到这个对象,使用迭代器就可以遍历这个对象的内部。

下面我们将iterator()方法单拿出来,通过代码具体化:

public static void testIterator() { //实现迭代器
        Collection collection = new ArrayList();
        collection.add(123);
        collection.add("AA");
        collection.add(new Date());
        collection.add("BB");
        collection.add(new Person("JJ",23));

        Iterator i = collection.iterator();
        while(i.hasNext()) {
            System.out.println(i.next());
        }
    }

创建一个迭代器的对象i,这个对象指向Collection对象所调用的iterator()方法。此时要注意,可以理解为i初始时指在集合的上边的位置,i.next()才指向第一个位置。

然后通过while循环,这里要注意判断条件是hasNext()方法,即如果有下一个元素,那么就打印下一个元素:i.next()。i.next()有两个作用:一是返回下一个元素,二是指针下移一位。

要注意下面这种方式是错误的:

Iterator i = collection.iterator();
while(i.next() != null) {
System.out.println(i.next());

表面上看,这么写是可以通过编译的,并且看起来也“似乎对”:如果i的next不是空,那么就打印i.next()。但是这么做犯的错误就是没有理解i.next()的两个作用。在while的判断阶段就下移了一位,进入循环后又下移了一位。相当于是间隔打印。并且倒数第二位的时候,判断最后一位存在,但是要返回最后一位的next,那么运行时会报错。

注意: Iterator必须依附于Collection对象,若有一个Iterator对象,则必然有一个与之关联的Collection对象。

foreach

除了可以使用Iterator接口迭代访问Collection集合里的元素之外,Java提供的foreach循环迭代访问集合元素会更加便捷:

public static void testForeach() {  //使用增强for循环
        Collection collection = new ArrayList();
        collection.add(123);
        collection.add("AA");
        collection.add(new Date());
        collection.add("BB");
        collection.add(new Person("JJ",23));

        for(Object o: collection) {
            System.out.println(o);
        }
    }

foreach的格式是这样的:

for(要输出的元素的类型Type 要输出的临时变量x : 要遍历的集合) {
    带x的语句;
}

使用foreach循环来迭代访问Collection集合里的元素更加简洁,这就是foreach循环带来的优势。

与使用Iterator接口迭代访问集合元素类似地是,foreach循环中的迭代变量也不是集合元素本身,系统只是依次把集合元素的值赋给迭代变量,因此,在foreach循环中修改迭代变量的值也没有任何实际意义。

Collection与Collections

与Array、Arrays类似,Collection、Collections直接也有着暧昧的关系。

在上面我们已经很清楚的认识了Collection接口,那么我们主要是看Collections类是什么。

java.util.Collections 是一个包装类。它包含有各种有关集合操作的静态多态方法。此类不能实例化,就像一个工具类,服务于Java的Collection框架。

public class Collectionsextends Object此类完全由在 collection 上进行操作或返回 collection 的静态方法组成。它包含在 collection 上操作的多态算法,即“包装器”,包装器返回由指定 collection 支持的新 collection,以及少数其他内容。

Iterable与Iterator

又是两个长得差不多的来辨析。

首先从名字上去理解:

Iterator: 在英语中or 结尾是都是“表示 …样的人 、… 者”。这里也是一样:iterator就是迭代器,它就是提供迭代机制的对象,具体如何迭代,都是Iterator接口规范的。

Iterable: 故名思议,实现了这个接口的集合对象支持迭代,是可迭代的。able结尾的表示 “能…样,可以做…”。

Iterator

Iterator我们之前已经了解了。

首先,我们还是先看下Iterator接口的定义:

//Iterator接口的JDK源码,注释为整理建议使用Iterator的正确姿势
public interface Iterator<E> {
    boolean hasNext();    //每次next之前,先调用此方法探测是否迭代到终点
    E next();            //返回当前迭代元素 ,同时,迭代游标后移
     /*删除最近一次已近迭代出去的那个元素。
     只有当next执行完后,才能调用remove函数。
     比如你要删除第一个元素,不能直接调用 remove(),而要先next一下();
     在没有先调用next 就调用remove方法是会抛出异常的。
     这个和MySQL中的ResultSet很类似
    */
    void remove() 
    {
        throw new UnsupportedOperationException("remove");
    }
}

其中:

  • Object next():返回当前迭代元素,同时,迭代游标后移,返回值是Object,需要强制转换成自己需要的类型。
  • boolean hasNext():判断容器内是否还有可供访问的元素。
  • void remove():删除最近一次已近迭代出出去的那个元素。

Iterator接口中的核心方法next(),hasNext(),remove(),都是依赖当前位置。如果这些集合直接实现Iterator接口,则势必导致集合对象中包含当前迭代位置的数据(指针)。当集合在不同方法间进行传递的时候,由于当前迭代位置不可知,所以next()的结果也不可知。除非再为Iterator接口添加一个reset()方法,用来重置当前迭代位置。

注意:

  1. 每次在迭代前,先调用hasNext()探测是否迭代到终点(本次还能再迭代吗?)。
  2. next方法不仅要返回当前元素,还要后移游标cursor
  3. remove()方法用来删除最近一次已经迭代出的元素
  4. 迭代出的元素是原集合中元素的拷贝(重要)
  5. 配合foreach使用

Iterable

Iterable接口 (Java.lang.Iterable) 是Java集合的顶级接口之一。Collection接口就继承于Iterable接口。

一个集合对象要表明自己支持迭代,能有使用foreach语句的特权,就必须实现Iterable接口,表明我是可迭代的!然而实现Iterable接口,就必需为foreach语句提供一个迭代器。
这个迭代器是用接口定义的iterator方法提供的。也就是iterator方法需要返回一个Iterator对象。

//Iterable JDK源码
//可以通过成员内部类,方法内部类,甚至匿名内部类去实现Iterator
public interface Iterable<T>
{
    Iterator<T> iterator();
}

Iterable接口中只包含一个方法,就是一个iterator()方法,用来返回一个Iterator类型的对象,或者说返回一个实现了Iterator接口的对象。

实现了Iterable接口的类可以拥有增强的for循环,即只要实现了Iterable接口的类,就可以使用Iterator迭代器了。如

public static <AnyType> void print(Collection<AnyType> coll)
{
    for(AnyType item: coll)
        System.out.println(item);
}

注意: 这里的函数输入参数Collection coll,其中coll是个接口变量。本来接口是不能用new来实例化一个接口的,即不能构造接口对象,但是可以用来声明接口变量,而且接口变量必须引用实现了接口的类对象。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值