Java集合框架

来源:互联网 发布:ajaxupload.js cdn 编辑:程序博客网 时间:2024/06/06 14:02

Java集合框架-概述

集合框架的由来

容器类出现的由来

集合框架出现之前,有容器类(Vector,Stack,Hashtable)。容器类的出现就是为了存放多个数据。

思考:
数组就可以存放多个数据,那么为什么还会有容器类的出现呢???
答案:
数组的弊端:
1. 数组一旦初始化之后,长度固定,不能改变。
2. 如果在N个地方存储多个数据,那么得专门编写相应的多个操作方法。这一点就没有体现出Java的“封装思想”。

集合框架出现的由来

思考:
既然存在容器类,为什么又会有集合框架的出现呢???
答案:
虽然这些容器类非常好用,但是却不能统一管理。集合框架的类和接口都存放在java.util包中。集合框架可以提供功能的复用以及可以让程序员专注于业务而非数据结构和算法。

什么是集合框架

集合框架是为表示和操作集合而规定的一种统一的标准的体系结构。任何一种集合框架都包含三个部分:对外的接口、接口的实现以及对集合运算的算法。

常用的集合类

  1. Set(集):集合中的对象不按特定方式排序,不允许元素重复.
  2. List(列表):集合中的对象按照索引位置排序,允许元素重复.
  3. Map(映射):集合中每一个元素都包含一对key和value对象.不允许key对象重复,值对象可以重复.

List接口

Vector类

在jdk2之前,要存储多个数据,有一个类叫“Vector”.

Vector类源代码

Vector类存储原理

通过源码分析,发现在Vector类中,有一个Object[]类型的数组。
可以得到
1. 表面上是把数据存储到Vector对象中,其实底层仍然是把数据存储到Object[]数组中。
2. 该数组的元素类型是object,因此可以知道集合中能存储任意类型的对象,但是不能存储基本数据类型的值(Java5之前,必须对基本数据类型手动装箱。如:v.addElement(Integer.valueOf(123))。在Java5之后,支持自动装箱,但是其实底层依然是手动装箱)。
3. 集合类中存储的都是对象的引用,而非对象本身。

Vector类的操作方法

增加:
boolean add(Object e) 将指定元素添加到此向量的末尾,等价于addElement方法。
void add(int index, Object element) 在此向量的指定位置插入指定的元素。
boolean addAll(Collection c) :把c集合中的元素添加到当前集合对象中.

删除:
Object remove(int index) :删除指定索引位置的元素,并返回删除之后的元素.
boolean remove(Object o):删除指定的元素.
boolean removeAll(Collection c):从此集合中移除包含在指定 集合c中的所有元素。
boolean retainAll(Collection c):在此集合中仅保留包含在指定 集合c中的元素,求两个集合的交集。

修改:
Object set(int index, Object element) :修改当前集合中指定索引位置的元素.
返回被替换的旧的元素.

查询:
int size() :返回当前集合中存储几个元素.
boolean isEmpty():判断当前集合中元素个数是否为0. (一般说集合为空是指集合里面没有元素,而不是集合的引用为空)
Object get(int index):查询指定索引位置的元素.
Object[] toArray():把集合对象转换为Object数组.

Stack类

基本概念

栈(数据结构的一种):LIFO(先进后出)
要来实现栈的存储,底层可以用数组也可以用链表。查看了API文档,发现栈继承了Vector,那么他的底层实现就是“数组”

操作方法

官方建议

官方建议:使用栈尽量使用ArrayDeque:Deque 接口及其实现提供了 LIFO 堆栈操作的更完整和更一致的 set,应该优先使用此 set,而非此类。例如:    Deque<Integer> stack = new ArrayDeque<Integer>();

ArrayList类

基本概念

ArrayList是java集合框架出现后用来取代Vector类。
二者底层原理都是基于数组的算法,一模一样。


区别:
Vector:所有的方法都使用了synchronized修饰符。 线程安全但是性能较低。
ArrayList:所有的方法都没有使用synchronized修饰符。 线程不安全但是性能较高。

即使以后在多线程环境下,我们也不使用Vector类。
ArrayList list = Collections.synchronizedList(new ArrayList(…));

常用方法

参照Vector阅读源代码发现,Vector和ArrayList的源代码差异有点大(从设计上考虑).有的时候某个方法需要返回一个ArrayList对象:     但是在该方法中,如果一个都没有查询到,我们不会返回null,我们会返回一个空集对象(没有元素的集合).     public ArrayList  getAll()     {             //TODO             //return  Collections.emptyList();//最好的方式.             return  new ArrayList();//但是很多人最直观,最简单选用的方式     }    在Java7之前,即使使用new ArrayList创建对象,一个元素都不存储,但是在堆空间依然初始化了长度位10的Object数组,没必要.    从Java7开始优化这个设计,new ArrayList,其实底层创建的使用一个空数组.                             Object [] elementData = new Object[]{};     在第一次调用add方法的时候,才会重新去初始化数组.

LinkedList类

LinkedList类是双向链表,单向队列,双向队列,栈的实现类:     LinkedList类实现单向队列和双向队列的接口,自身提高了栈操作的方法,链表操作的方法.     在LinkedList类中存在很多方法,但是功能都是相同的.LinkedList表示了多种数据结构的实现,每一种数据结构的操作名字不同.-------------------------------------------------------------------------LinkedList类是线程不安全的类,在多线程环境下所有保证线程安全.LinkedList list = Collections.synchronizedList(new LinkedList(...));

面试题:编写一个双向链表

Java集合框架-重构设计

List实现类性能和特点分析

共同的特点

1. 允许元素重复2. 记录元素的先后顺序

区别

Vector类:底层采用数组结构算法,方法都使用了Synchronized修饰,线程安全。ArrayList类:底层采用数组结构算法,方法没有使用Synchronized修饰,线程不安全。LinkedList类:底层采用双向链表结构算法,方法没有使用Synchronized修饰,线程不安全。

数组结构算法和双向链表结构算法性能分析

数组结构算法:插入和删除速度慢,查询和修改较快。双向链表结构算法:插入和删除速度快,查询和修改较慢。

迭代集合

概念

把集合做的元素一个一个的遍历取出来.

迭代器对象

Iterator:迭代器对象,只能从上往下迭代。    返回值为boolean的hasNext();        判断当前指针后是否有下一个元素。    返回值为Object的next();        1、获取指针的下一个元素。        2、把指针向下移动。ListIterator:  是Iterator接口的子接口,支持双向迭代,从上往下迭代,从下往上迭代.Enumeration:古老的迭代器对象,现在已经被Iterator取代了. 适用于古老的Vector类.

深入分析for-each和迭代器

foreach语法格式

for-each的语法格式:for(类型 变量 :数组名/Iterable的实例){    //TODO}

1、foreach可以操作数组:底层依然采用的for循环+索引获取数组元素。

2、foreach可以操作Iterable的实例:底层其实采用的Iterator(迭代器).

使用foreach迭代数组和集合元素情况

一般情况下都可以直接使用foreach迭代数组和集合元素即可,简单。但是,当需要边迭代集合元素,边删除指定元素时:此时只能使用迭代器。而且只能使用迭代器的remove方法。该方法会从两个线程中同时移除被删除的元素,.保证了两个线程的同步.

    Iterator it = list.iterator();    while(it.hasNext()){        Object obj = it.next();        if("C".equals(obj)){            it.remove();        }       }    System.out.println(list);

Set接口

HashSet类

HashSet是Set接口最常用的实现类,顾名思义,底层才用了哈希表(散列/hash)算法.
其底层其实也是一个数组,存在的意义是提供查询速度,插入速度也比较快,但是适用于少量数据的插入操作.

在HashSet中如何判断两个对象是否相同的问题

  1. 两个对象的equals比较相等,返回true
  2. 两个对象的hashCode方法返回值相等

二者缺一不可.

不同数据类型如何来计算hashCode的值

LinkedHashSet类

List接口:允许元素重复,记录先后添加顺序.

Set接口: 不允许元素重复,不记录先后添加顺序.

需求: 不允许元素重复,但是需要保证先后添加的顺序.

LinkedHashSet:**底层才有哈希表和链表算法**.    哈希表:来保证唯一性,.此时就是HashSet,在哈希表中元素没有先后顺序.    链表:  来记录元素的先后添加顺序.

TreeSet类

基本概念

底层采用红黑树算法,会对存储元素从大到小排序。因此,TreeSet存储数据的类型必须一致。

TreeSet的排序规则

自然排序TreeSet调用集合元素的compareTo方法来比较元素的大小关系,然后从小到大排序。
注意:要求TreeSet集合中元素得实现java.util.Comparable接口。

java.util.Comparable

覆盖public int compareTo(Object o)方法,比较当前对象(this)和参数对象(o)。
this > o :返回正整数1
this < o :返回负整数-1
this = o :返回0 此时认为两个对象是同一个对象

定制排序

对于TreeSet集合来说,要么使用自然排序,要么使用定制排序.
判断两个对象是否相等的规则:
自然排序: compareTo方法返回0;
定制排序: compare方法返回0;

Set实现类性能对比

共同特点:

  1. 都不允许元素重复
  2. 都不是线程安全的
    解决线程不安全的方案:Set s = Collections.synchronizedSet(Set对象);

HashSet:

底层采用的是哈希表算法,查询效率极高。

判断两个对象是否相等的规则:

  1. equals比较为true
  2. hashCode的值相同

要求:要求存在哈希表中的对象元素都得覆盖equals和hashCode方法。

LinkedHashSet:

HashSet的子类,底层采用的是哈希表算法,但是也使用了链表算法来维持元素的先后添加顺序。

判断两个对象是否相等的规则与HashSet相同。

TreeSet:

不保证元素的先后添加顺序,但是会对集合中的元素做排序操作
底层采用红黑树算法(树结构)

自然排序: 要求在TreeSet集合中的对象必须实现java.lang.Comparable接口,并覆盖compareTo方法.

定制排序: 要求在构建TreeSet对象的时候,传入一个比较器对象(必须实现java.lang.Comparator接口).
在比较器中覆盖compare方法,并编写比较规则.

TreeSet判断元素对象重复的规则:
compareTo/compare方法是否返回0.如果返回0,则视为是同一个对象.


HashSet做等值查询效率高,TreeSet做范围查询效率高.
而我们更多的情况,都是做等值查询, 在数据库的索引中做范围查询较多,所以数结构主要用于做索引,用来提高查询效率.

Map接口

因为Map接口既没有继承于Collection接口,也没有继承于Iterable接口,所以不能对Map直接使用for-each操作。

Map的操作方法

Set和Map之间的关系

发现在Map和Set中有很多相类似的实现类名:

Set Map 算法

HashSet HashMap 哈希表
TreeSet TreeMap 红黑树
LinkedHashSet LinkedHashMap 哈希表/链表

等…

如果集合前缀相同,说明底层算法是一样的,现在单独使用HashSet和HashMap来研究.
通过阅读源代码:发现,相同算法的Set底层用的是相同算法的Map.
把Set的集合对象作为Map的key,再使用一个Object常量最为value.

Map的实现类

Map常用实现类

HashMap:采用哈希表算法, 此时Map中的key**不会保证添加的先后顺序**,key也不允许重复.
key判断重复的标准是: key1和key2是否equals为true,并且hashCode相等.

TreeMap:采用红黑树算法,此时Map中的key会按照自然顺序或定制排序进行排序,,key也不允许重复.
key判断重复的标准是: compareTo/compare的返回值是否为0.

LinkedHashMap: 采用链表和哈希表算法,此时Map中的key**会保证先后添加的顺序**,key不允许重复.
key判断重复的标准和HashMap中的key的标准相同.

Hashtable:
采用哈希表算法,是HashMap的前身(类似于Vector是ArrayList的前身).打死不用.
在Java的集合框架之前,表示映射关系就使用Hashtable.
所有的方法都使用synchronized修饰符,线程安全的,但是性能相对HashMap较低.

Properties:
Hashtable的子类,此时要求key和value都是String类型.
用来加载资源文件(properties文件(IO再讲)).


HashMap和TreeMap以及LinkedHashMap都是线程安全的,但是性能较高。

解决方案:Map m = Collections.synchronizedMap(Map对象)

哈希表算法,做等值查询最快
数据结构算法:做范围查询最快->应用到索引上

List,Set以及Map如何选用

选取原则取决于每一种容器的存储特点以及当前业务需求。

List:单一元素集合

允许元素重复/记录元素的添加顺序。

Set:单一元素集合

不允许元素重复/不记录元素的添加顺序。

集合工具类

Arrays类

toArray:将集合转为数组

Object[] obj = 集合对象.toArray();

asList:将数组转为(List)集合
public static List asList(T… a)

List list = Arrays.asList("A","B","C","D");

Collections类

面试题:Collection和Collections的区别。


Collections类:封装了Set,List,Map的操作的工具方法.
获取空集对象(没有元素的集合,注意集合不为null):

常用的集合类:
HashSet/ArrayList/HashMap都是线程不安全的,在多线程环境下不安全.
在Collections类中有获取线程安全的集合方法:

    List list = Collections.synchronizedList(new ArrayList());        当要做迭代的时候得使用synchronized.       synchronized(list) {          TODO    }    Set  set = Collections.synchronizedSet(new HashSet());    Map map = Collections.synchronizedMap(new HashMap());

小结

原创粉丝点击