Java学习笔记-《Java程序员面试宝典》-第四章基础知识-4.9容器(4.9.1-4.9.3)

本文详细介绍了Java集合框架的组成部分,包括List、Set、Map等核心接口及其典型实现类的特点与应用场景,并对比了ArrayList、Vector与LinkedList的区别。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

4.9.1Java Collections框架是什么

Java Collection框架中包含了大量结合接口以及这些接口的实现类和操作他们的算法(例如排序、查找、反转、替换等),具体而言,主要提供了List(列表)、Queue(列表)、Set(集合)、Stack(栈)和Map(映射表,用于存放键值对)等数据结构。其中,List、Quebue、Set、Stack都继承自Collection接口。
Collection是整个集合框架的基础,它里面存储一组对象,表示不同类型的Collections,它的作用只是提供维护一组对象的基本接口而已。
下面分别介绍Set、List、和Map三个接口。
1>Set表示数学意义上的集合概念。其最主要的特点是集合中的元素不能重复,因此存入Set的每个元素都必须定义equals()方法确保对象的唯一性。该接口有两个实现类:HashSet和TreeSet。其中TreeSet实现了SortedSet接口,因此TreeSet容器中的元素是有序的。
2>List又称为有序的Collection。它按对象进入的顺序保存对象,所以它能对列表中的每个元素的插入和删除位置进行精确的控制。同时,它可以保存重复的对象。LinkedList、ArrayList和Vector都实现了List接口。
3>Map提供了一个从键映射到值的数据结构。它用于保存键值对,其中值可以重复,但键是唯一的,不能重复。Java类库中有多个实现该接口的类:HashMap、TreeMap、LinkedHashMap、WeakHashMap和IdentityHashMap。虽然它们都实现了相同的接口,但执行效率却不是完全相同的。具体而言,HashMap是基于散列表实现的,采用对象的HashCode可以进行快速查询。LinkedHashMap采用列表来维护内部的顺序。TreeMap基于红黑树的数据结构来实现的,内部元素是按需排列的。

4.9.2什么是迭代器

迭代器(Iterator)是一个对象,它的工作是遍历并选择序列中的对象,它提供了一种访问一个容器(container)对象中的各个元素,而又不必暴露该对象内部细节的方法。通过迭代器,开发人员不需要了解容器底层的结构,就可以实现对容器的遍历。由于创建迭代器的代价小,因此迭代器通常被称为轻量级的容器。
迭代器的使用主要有以下3个方面的注意事项:
1>使用容器的iterator()方法返回一个iterator,然后通过Iterator的next()方法返回第一个元素。
2>使用Iterator的hasNext()方法判断容器中是否还有元素,如果有,可以使用next()方法获取下一个元素。
3>可以通过remove()方法删除迭代器返回的元素。
Iterator支持派生的兄弟成员。ListIterator只存在于List中,支持在迭代期间向List中添加或删除元素,并且可以在List中双向滚动。示例如下:

import java.util.*;

public class TestIterator {

    public static void main(String[] args){

        List<String> ls = new LinkedList<String>();

        ls.add("First");
        ls.add("Second");
        ls.add("Third");
        ls.add("Fourth");

        for(Iterator<String> iter = ls.iterator();iter.hasNext();){
            String str = (String) iter.next();
            System.out.println(str);
        }

    }
}

运行结果:
First
Second
Third
Fourth

在使用iterator()方法时经常会遇到ConcurrentModificationException异常,这通常是由于在使用Iterator遍历容器的同时又对容器做增加或删除操作所导致的,或者由于多线程操作导致,当一个线程使用迭代器遍历容器的同时,另外一个线程对这个容器进行增加或者删除操作。下例主要介绍单线程抛出ConcurrentModificationException的情况:

import java.util.*;

public class TestIterator {

    public static void main(String[] args){

        List<String> ls = new LinkedList<String>();

        ls.add("First");
        ls.add("Second");
        ls.add("Third");
        ls.add("Fourth");

        for(Iterator<String> iter = ls.iterator();iter.hasNext();){
            String str = (String) iter.next();
            System.out.println(str);
            if(str.equals("Second"))
                ls.add("Five");
        }

    }
}


运行结果:
First
Second
Exception in thread "main" java.util.ConcurrentModificationException
    at java.util.LinkedList$ListItr.checkForComodification(LinkedList.java:953)
    at java.util.LinkedList$ListItr.next(LinkedList.java:886)
    at com.LearningJava.pro20170623.TestIterator.main(TestIterator.java:17)

抛出上述异常的主要原因是当调用容器的iterator()方法返回Iterator对象时,把容器中包含对象的个数复制给了一个变量expectedModCount,在调用next()方法时会比较变量exceptedModCount与容器中实际对象的个数modCount的值是否相等,若二者不相等,则会抛出ConcurrentModificationException异常。因此在使用Iterator遍历容器的过程中,如果对容器进行增加或删除操作,
就会改变容器中对象的数量,从而导致抛出异常。解决方法如下:在遍历过程中把需要删除的对象保存到一个集合中,等遍历结束后再调用removeAll()方法来删除,或者使用iter.remove()方法。
示例如下:

import java.util.*;

public class TestIterator {

    public static void main(String[] args){

        List<String> ls = new LinkedList<String>();
        List<String> ls1 = new LinkedList<String>();

        ls.add("First");
        ls.add("Second");
        ls.add("Third");
        ls.add("Fourth");

        for(Iterator<String> iter = ls.iterator();iter.hasNext();){
            String str = (String) iter.next();
            System.out.println(str);
            if(str.equals("Second"))
            {
                ls1.add("Fifth");
                ls1.add("Sixth");
            }
        }

        ls.addAll(ls1);
        System.out.println("after insert");
        for(Iterator<String> iter = ls.iterator();iter.hasNext();){
            String str = (String) iter.next();
            System.out.print(" "+str);
        }

    }
}

运行结果:
First
Second
Third
Fourth
after insert
 First Second Third Fourth Fifth Sixth

以上主要介绍了单线程的解决方案,那么多线程访问容器的过程中抛出ConcurrentModificationException异常又该怎么解决呢?
1>在JDK 1.5版本引入了线程安全的容器,比如ConcurrentHashMap和CopyOnWriteArrayList等。可以使用这些线程安全的容器来代替非线程安全的容器。
2>在使用迭代器遍历容器时对容器的操作放到synchronized代码块中,但是当引用程序并发程度比较高时,这会严重影响程序的性能。

引申:Iterator与ListIterator有什么区别?
参见博客 http://blog.youkuaiyun.com/longshengguoji/article/details/41551491

4.9.3ArrayList,Vector和LinkedList有什么区别

ArrayList、Vector、LinkedList类均在java.util包中,均为可伸缩数组,即可以动态改变长度的数组。
ArrayList和Vector都是基于存储元素的Object[] array来实现的,它们会在内存中开辟出一块连续的空间来存储,由于数据存储是连续的,因此,它们支持用序号(下标)来访问元素,同时索引数据的速度比较快。但是在插入元素时需要移动容器中的元素,所以对数据的插入操作执行得比较慢。ArrayList和Vector都有一个初始化的容量大小,当里面存储的元素超过这个大小时就需要动态的扩充他们的存储空间。为了提高程序的效率,每次扩充容量,不是简单地扩充一个存储单元,而是一次增加多个存储单元。Vector默认扩充为原来的2倍(每次扩充空间的大小是可以设置的),而ArrayList默认扩充为原来的1.5倍(没有提供方法来设置空间扩充的方法)。
ArrayList与Vector最大的区别就是synchronization(同步)的使用,没有一个ArrayList的方法是同步的,而Vector的绝大多数方法都是直接或简介同步的,所以Vector是线程安全的,ArrayList不是线程安全的。正是由于实现同步要牺牲性能,所以Vector访问要比ArrayList慢。
LinkedList是采用双向列表来实现的,对数据的索引需要从列表头开始遍历,因此用于随机访问则效率比较低,但是插入元素时不需要对数据进行移动,因此插入效率较高。同时,LinkedList是非线程安全的容器。
那么,在实际使用时,如何从这几种容器中选择适合的使用呢?当对数据的主要操作为索引或只在集合的末端增加、删除元素时,使用ArrayList或Vector效率较高;当对数据的操作主要为指定位置的输入或删除操作时,使用LinkedList效率比较高;当在多线程中使用容器时(即多个线程会同时访问该容器),选用Vector较为安全。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值