Java总结系列之集合体系

来源:互联网 发布:女生做seo 编辑:程序博客网 时间:2024/05/22 11:39
集合类的由来:
对象用于封装特有数据,对象多了需要存储,如果对象的个数不确定。
就使用集合容器进行存储。

集合特点:
1,用于存储对象的容器。
2,集合的长度是可变的。
3,集合中不可以存储基本数据类型值。 

集合容器因为内部的数据结构不同,有多种具体容器。
不断的向上抽取,就形成了集合框架。

Java.lang: Java中的核心包。

java.util :Java的工具包。

java.util中的接口 Collection<E>:集合框架中的根接口

虚线框中都是接口,实线框是已实现方法的子类。


Collection的常见方法:

1,添加。
boolean add(Object obj):
boolean addAll(Collection coll):
2,删除。
boolean remove(object obj):
boolean removeAll(Collection coll);
void clear();
3,判断:
boolean contains(object obj):
boolean containsAll(Colllection coll);
boolean isEmpty():判断集合中是否有元素。
4,获取:
int size():
Iterator iterator():取出元素的方式:迭代器。
该对象必须依赖于具体容器,因为每一个容器的数据结构都不同。
所以该迭代器对象是在容器中进行内部实现的。
对于使用容器者而言,具体的实现不重要,只要通过容器获取到该实现的迭代器的对象即可,
也就是iterator方法。

Iterator接口就是对所有的Collection容器进行元素取出的公共接口。
其实就是抓娃娃游戏机中的夹子!
5,其他:
boolean retainAll(Collection coll);取交集。

Object[] toArray():将集合转成数组。 

关键知识点:

获取:iterator():迭代器

Iterator iterator():取出元素的方式:迭代器。返回值是迭代器对象。

 

Iterator接口就是对所有的Collection容器进行元素取出的公共接口。

其实就相当于抓娃娃游戏机中的夹子!

Collection容器就相当于游戏机机箱,容器中的元素就相当于娃娃。

Iterator接口定义方法就相当于操纵杆和按钮。在不同的容器中

实现接口的方法的方式不同,就相当于不同的游戏机中不同的夹子

(三抓或两爪)与我们无关,我们只需操作操作杆与按钮就可以了。


-------------------------------
Collection
|--List:有序(存入和取出的顺序一致),元素都有索引(角标),元素可以重复。
|--Set:元素不能重复,无序。

List:特有的常见方法:有一个共性特点就是都可以操作角标。

1,添加
void add(index,element);
void add(index,collection);
2,删除;
Object remove(index):
3,修改:
Object set(index,element);

4,获取:
Object get(index);
int indexOf(object);
int lastIndexOf(object);
List subList(from,to);

list集合是可以完成对元素的增删改查。

List:
|--Vector:内部是数组数据结构,是同步的。增删,查询都很慢!

|--ArrayList:内部是数组数据结构,是不同步的。替代了Vector。查询的速度快,时间复杂度为O(1)

|--LinkedList:内部是链表数据结构,是不同步的。增删元素的速度很快。时间复杂度为O(1)

 

使用LinkedList来模拟一个堆栈或者队列数据结构。

  堆栈:先进后出 First In Last Out  FILO

  队列:先进先出 First In First Out FIFO就像管子。

  我们应该描述这样一个容器,给使用提供一个容器对象完成这两种结构中的一种。

其实很简单,我们使用LinkedList来模仿一个队列的话,只需要两部

第一步:定义一个队列对象,用来描述队列这样一个数据结构,内部我么封装LinkedList

第二步:我是定义一个队列的add()方法,然后使用LinkedList的addLast(Object o)来实现只能从队列尾部添加元素

第三步:我们定义一个remove()方法,然后使用LinkedList的removeFirst(Object o)来实现只能从队列头部删除元素的功能

---------------------------------------------------------------------------------------------------

另一大部分:
Set:元素不可以重复,是无序。
Set接口中的方法和Collection一致。
|--HashSet: 内部数据结构是哈希表 ,是不同步的。
如何保证该集合的元素唯一性呢?
是通过对象的hashCode和equals方法来完成对象唯一性的。
如果对象的hashCode值不同,那么不用判断equals方法,就直接存储到哈希表中。 
如果对象的hashCode值相同,那么要再次判断对象的equals方法是否为true。
如果为true,视为相同元素,不存。如果为false,那么视为不同元素,就进行存储。

记住:如果元素要存储到HashSet集合中,必须覆盖hashCode方法和equals方法。
一般情况下,如果定义的类会产生很多对象,比如人,学生,书,通常都需要覆盖equals,hashCode方法。
建立对象判断是否相同的依据。

哈希表存储结构的特点是:

哈希算法来计算每一个对象在储存时应该储存在何处,然后以后

查找这个元素时直接使用其哈希来寻找这个元素,时间复杂度为O(1)

 

HashSet通过对象的hashCodeequals方法来完成对象唯一性的

:先判断哈希值hashCode方法若相同再判断内容equals方法,注意要保证这两个方法返回值相同,否则可能出现内存泄漏


知识点哈希表确定元素是否相同
1,判断的是两个元素的哈希值是否相同。如果相同,在判断两个对象的内容是否相同。
2,判断哈希值相同,其实判断的是对象的hashCode的方法。判断内容相同,用的是equals方法。

注意:如果哈希值不同,是不需要判断equals。
  

HashSet下面有一个子类,既可以保证唯一,又可以保证顺序

LinkedHashSet

HashSet hs = new LinkedHashSet();

具有可预知迭代顺序的 Set 接口的哈希表和链接列表实现,,也就是说其在

储存数据时,及使用了哈希表保证唯一,又使用了链接列表保证顺序。


Treeset使用二叉树存储结构的特性对元素进行排序

我可以进行排序动作,我的排序是二叉树排序是根据数据比较大小之后的返回值来排序的并且我需要的返回值是正三种。为则放在右边,为负放左边,为零重复,不允许放。

但是我不具备比较方法就是说我不知道这个元素与现有元素那个大那个小,所以要根据比较方法返回的结果来进行排序


|--TreeSet:可以对Set集合中的元素进行排序。是不同步的。 
判断元素唯一性的方式:就是根据比较方法的返回结果是否是0,是0,就是相同元素,不存。 

TreeSet对元素进行排序的方式一:
让元素自身具备比较功能,元就需要实现Comparable接口。覆盖compareTo方法。
程序例子详见
如果不要按照对象中具备的自然顺序进行排序。如果对象中不具备自然顺序。怎么办?
可以使用TreeSet集合第二种排序方式二:
让集合自身具备比较功能,定义一个类实现Comparator接口,覆盖compare方法。
将该类对象作为参数传递给TreeSet集合的构造函数。

自然排序方法:实现接口第一种排序方法:

Public class Personimplements Comparable

也就是说必须自己实现接口Comparable,并定义compareTo方法定义出

自己特有的排序方法。

基于 TreeMap  NavigableSet 实现。使用元素的自然顺序对元素进行排序,

或者根据创建 set 时提供的 Comparator 进行排序,具体取决于使用的构造方法。

java.lang 接口 Comparable<T>

类型参数: T - 可以与此对象进行比较的那些对象的类型

此接口强行对实现它的每个类的对象进行整体排序。这种排序被称为

类的自然排序,类的 compareTo 方法被称为它的自然比较方法。

用法:类在存储数据时,需要对存储的数据进行比较进而得出其储存位置,这个接口就是可以给一些没有比较功能的类提供比较功能,进而强行对实现它的每个类的对象进行整体排序。这种排序被称为类的自然排序。对于实现它的每个类,可以在类中对这个方法进行覆盖,进而定义出自己特有的排序方法。

类的 compareTo 方法被称为它的自然比较方法。

对于实现它的每个类,可以在类中对这个方法进行覆盖,进而定义出

自己特有的排序方法。

TreeSet集合第二种排序方式:

 让集合自身具备比较功能就是定义一个比较器,然后带入集合初始化

java.util 接口 Comparator<T>

类型参数: 

T - 此 Comparator 可以比较的对象类型

其中的比较方法是:同时接收两个参数进行比较

int compare(T o1,T o2) 
        o1与o2是 : 比较用来排序的两个参数

 

应用特有的排序方法:自定义构造器(比较器)

TreeSet ts =new TreeSet(new ComparatorByName());

构造函数中传递构造器。

ComparatorByName():创建了一个根据Person类的name进行排序的比较器。

class ComparatorByNameimplements Comparator<Person>

只要对象想进行比较,就实现这个Comparator接口就连字符串String

其中的比较方法也是实现接口,进而覆盖compare方法得到的


程序小例子

对字符串进行长度排序。“zz,abc,cba,nbaq,aaaaa”

TreeSet ts = new TreeSet(new ComparatorByLength());

因为字符串对象自己的自然排序是按照首字母排序,

“aaaaa ,abc ,cba ,nbaq ,zz”而我们不可能去修改String类中

compareTo方法,因此排序的第一种方法已不适用,所以我们使用第二种方法即:

要定义一个构造器,在构造器中覆盖compareTo方法,定义出符合要求的排序方法。



首先我们来看一下继承关系:![这里写图片描述](http://img.blog.csdn.net/20160408150531095) - 我们可以看出ArrayList、LinkedList、Vector都实现了List的接口。  

接下来分别看一下三个数据结构的说明。

>public class **ArrayList<E>** extends AbstractList<E>implements List<E>, RandomAccess, Cloneable, Serializable>List 接口的**大小可变数组**的实现。实现了所有可选列表操作,并**允许包括 null 在内的所有元素**

除了实现 List 接口外,此类还提供一些方法来操作内部用来存储列表的数组的大小。(此类大致上等同于 Vector 类,除了**此类是不同步的**。)>

每个 ArrayList 实例都有一个容量。该容量是指用来存储列表元素的数组的大小。它总是至少等于列表的大小。随着向 ArrayList 中不断添加元素,其容量也自动增长。并未指定增长策略的细节,因为这不只是添加元素会带来分摊固定时间开销那样简单。

>在添加大量元素前,应用程序可以使用 ensureCapacity 操作来增加 ArrayList 实例的容量。这可以减少递增式再分配的数量。>  

List list = Collections.synchronizedList(new ArrayList(...));  

此类的 iterator 和 listIterator 方法返回的迭代器是快速失败的:在创建迭代器之后,除非通过迭代器自身的 remove 或 add 方法从结构上对列表进行修改,否则在任何时间以任何方式对列表进行修改,迭代器都会抛出 ConcurrentModificationException。因此,面对并发的修改,迭代器很快就会完全失败,而不是冒着在将来某个不确定时间发生任意不确定行为的风险。

注意,迭代器的快速失败行为无法得到保证,因为一般来说,不可能对是否出现不同步并发修改做出任何硬性保证。快速失败迭代器会尽最大努力抛出 ConcurrentModificationException。

因此,为提高这类迭代器的正确性而编写一个依赖于此异常的程序是错误的做法:迭代器的快速失败行为应该仅用于检测 bug。

然后是LinkedList>public class **LinkedList**<E> extends AbstractSequentialList<E>implements List<E>, Deque<E>, Cloneable, Serializable>List 接口的链接列表实现。实现所有可选的列表操作,并且允许所有元素(**包括 null**)。

除了实现 List 接口外,LinkedList 类还为在列表的开头及结尾 get、remove 和 insert 元素提供了统一的命名方法。这些操作允许将链接列表用作堆栈、队列或双端队列。

此类实现 Deque 接口,为 add、poll 提供先进先出队列操作,以及其他堆栈和双端队列操作。>所有操作都是按照**双重链接列表**的需要执行的。在列表中编索引的操作将从开头或结尾遍历列表(从靠近指定索引的一端)。>注意,此实现**不是同步**的。如果多个线程同时访问一个链接列表,而其中至少一个线程从结构上修改了该列表,则它必须 保持外部同步。(结构修改指添加或删除一个或多个元素的任何操作;仅设置元素的值不是结构修改。)这一般通过**对自然封装该列表的对象进行同步操作来完成**

如果不存在这样的对象,则应该使用 Collections.synchronizedList 方法来“包装”该列表。最好在创建时完成这一操作,以防止对列表进行意外的不同步访问,

如下所示:

List list = Collections.synchronizedList(new LinkedList(...)); >

此类的 iterator 和 listIterator 方法返回的迭代器是快速失败 的:在迭代器创建之后,如果从结构上对列表进行修改,除非通过迭代器自身的 remove 或 add 方法,其他任何时间任何方式的修改,迭代器都将抛出 ConcurrentModificationException。因此,面对并发的修改,迭代器很快就会完全失败,而不冒将来不确定的时间任意发生不确定行为的风险。

注意,迭代器的快速失败行为不能得到保证,一般来说,存在不同步的并发修改时,不可能作出任何硬性保证。快速失败迭代器尽最大努力抛出 ConcurrentModificationException。因此,编写依赖于此异常的程序的方式是错误的,正确做法是:迭代器的快速失败行为应该仅用于检测程序错误。

最后是Vector>public class **Vector**<E> extends AbstractList<E>implements List<E>, RandomAccess, Cloneable, Serializable>Vector 类可以**实现可增长的对象数组**

与数组一样,它包含可以使用整数索引进行访问的组件。但是,Vector 的大小可以根据需要增大或缩小,以适应创建 Vector 后进行添加或移除项的操作。

每个向量会试图通过维护 capacity 和 capacityIncrement 来优化存储管理。capacity 始终至少应与向量的大小相等;这个值通常比后者大些,因为随着将组件添加到向量中,其存储将按 capacityIncrement 的大小增加存储块。应用程序可以在插入大量组件前增加向量的容量;这样就减少了增加的重分配的量。

由 Vector 的 iterator 和 listIterator 方法所返回的迭代器是快速失败的:如果在迭代器创建后的任意时间从结构上修改了向量(通过迭代器自身的 remove 或 add 方法之外的任何其他方式),则迭代器将抛出 ConcurrentModificationException。

因此,面对并发的修改,迭代器很快就完全失败,而不是冒着在将来不确定的时间任意发生不确定行为的风险。Vector 的 elements 方法返回的 Enumeration 不是 快速失败的。

注意,迭代器的快速失败行为不能得到保证,一般来说,存在不同步的并发修改时,不可能作出任何坚决的保证。快速失败迭代器尽最大努力抛出 ConcurrentModificationException。

因此,编写依赖于此异常的程序的方式是错误的,正确做法是:迭代器的快速失败行为应该仅用于检测 bug。>从 Java 2 平台 v1.2 开始,此类改进为可以实现 List 接口,使它成为 Java Collections Framework 的成员。与新 collection 实现不同,**Vector 是同步的**

-区别 ArrayList 本质上是一个可改变大小的**数组**.当元素加入时,其大小将会动态地增长.内部的元素可以直接通过get与set方法进行访问.元素顺序存储 ,**随机访问很快,删除非头尾元素慢,新增元素慢而且费资源** ,较适用于无频繁增删的情况 ,比数组效率低,如果不是需要可变数组,可考虑使用数组 ,**非线程安全**.

LinkedList 是一个**双链表**,在添加和删除元素时具有比ArrayList更好的性能.但在get与set方面弱于ArrayList. 适用于 :没有大规模的随机读取,大量的增加/删除操作.**随机访问很慢,增删操作很快**,不耗费多余资源 ,允许null元素,**非线程安全.**

Vector (类似于ArrayList)但其是**同步**的,开销就比ArrayList要大。如果你的程序本身是线程安全的,那么使用ArrayList是更好的选择。Vector和ArrayList在更多元素添加进来时会请求更大的空间。Vector每次请求其大小的双倍空间,而ArrayList每次对size增长50%.     






0 0