您的位置:首页 > 编程语言 > Java开发

Java基础回顾--jav集合1 集合的分类、解析

2017-04-10 13:06 218 查看

1. 集合

1.1. 什么是集合 

存储对象的容器,面向对象语言对事物的体现都是以对象的形式,所以为了方便对多个对象的操作,存储对象,集合是存储对象最常用的一种方式。

集合的出现就是为了持有对象。集合中可以存储任意类型的对象, 而且长度可变。在程序中有可能无法预先知道需要多少个对象, 那么用数组来装对象的话, 长度不好定义, 而集合解决了这样的问题。(简单来说就是存放对象的容器,但比数组要好得多,因为长度可以变化,提供多种方法)

1.1. 集合和数组的区别

数组和集合类都是容器:

数组长度是固定的,集合长度是可变的。数组中可以存储基本数据类型,集合只能存储对象数组中存储数据类型是单一的,集合中可以存储任意类型的对象。

集合类的特点:

用于存储对象,长度是可变的,可以存储不同类型的对象。

 

1.1.1. 数组的缺点

存储类型单一的数据容器,操作复杂(数组一旦声明好不可变)(即不好进行CURD,也就是增删查改)

 

1.2. 集合的分类

集合能做什么:

1:将对象添加到集合

2:从集合中删除对象

3: 从集合中查找一个对象

4:从集合中修改一个对象就是增删改查

注意:集合和数组中存放的都是对象的引用而非对象本身

题外话: Java工程师对不同的容器进行了定义,虽然容器不同,但是还是有一些共性可以抽取最后抽取了一个顶层接口,那么就形成了一个集合框架。如何学习呢?当然是从顶层学起,顶层里边具有最共性,最基本的行为。具体的使用,就要选择具体的容器了。为什么? 因为不断向上抽取的东西有可能是不能创建对象的.抽象的可能性很大,并且子类对象的方法更多一些. 所以是看顶层,创建底层。那么集合的顶层是什么呢
叫做Collection

集合框架体系(一开始看这个图头都炸了,学完再回来看就好了。)



每一个容器对数据的存储方式不同,这种存储方式称之为数据结构(data
structure)

常用集合的树状分类:

---|Collection: 单列集合

---|List: 有存储顺序, 可重复

---|ArrayList: 数组实现, 查找快, 增删慢(由于是数组实现, 在增和删的时候会牵扯到数组增容, 以及拷贝元素. 所以慢。数组是可以直接按索引查 
 找, 所以查找时较快)

  ---|LinkedList: 链表实现, 增删快, 查找慢(由于链表实现, 增加时只要让前一个元素记住自己就可以, 删除时让前一个元素记住后一个元素, 后一个元 
 素记住前一个元素. 这样的增删效率较高但查询时需要一个一个的遍历, 所以效率较低)

                      ---|Vector: 和ArrayList原理相同, 但线程安全, 效率略低和ArrayList实现方式相同, 但考虑了线程安全问题, 所以效率略低

---|Set: 无存储顺序, 不可重复

---|HashSet

---|TreeSet

---|LinkedHashSet

---| Map: 键值对(双列集合)

---|HashMap

---|TreeMap

---|HashTable

---|LinkedHashMap
 

1.4什么时候该使用什么样的集合

Collection
我们需要保存若干个对象的时候使用集合。
 

List

 

 
如果我们需要保留存储顺序, 并且保留重复元素, 使用List.

如果查询较多, 那么使用ArrayList

如果存取较多, 那么使用LinkedList

如果需要线程安全, 那么使用Vector
 

 

Set

 
如果我们不需要保留存储顺序, 并且需要去掉重复元素, 使用Set.

如果我们需要将元素排序, 那么使用TreeSet

如果我们不需要排序, 使用HashSet, HashSet比

TreeSet效率高.

如果我们需要保留存储顺序, 又要过滤重复元素, 那么使用LinkedHashSet

2. 集合类(Collection)  

说完集合的概念和使用的选择,下面来回忆一下集合的接口Collection

Collection接口有两个子接口:

List(链表|线性表) 可存放重复元素,元素存取是有序的

Set(集)  不可以存放重复元素,元素存取是无序的

Collection特点:

Collection中描述的是集合共有的功能(CRUD)

学习集合对象 :

学习Collection中的共性方法,多个容器在不断向上抽取就出现了该体系。发现Collection接口中具有所有容器都具备的共性方法。查阅API时,就可以直接看该接口中的方法。并创建其子类对象对集合进行基本应用。当要使用集合对象中特有的方法,在查看子类具体内容。

查看api 文档Collection在在java.util 中(注意是大写Collection)

注意在现阶段遇到的 E T 之类的类型,需要暂时理解为object  因为涉及到了泛型.

创建集合对象,使用Collection中的List的具体实现类ArrayList

例如:Collection coll=new Arraylist();

2.1. Collection接口的共性方法

增加:

1:add() 将指定对象存储到容器中

                      add 方法的参数类型是Object 便于接收任意对象

2:addAll() 将指定集合中的元素添加到调用该方法和集合中

删除:

3:remove() 将指定的对象从集合中删除

4:removeAll() 将指定集合中的元素删除

修改

5:clear() 清空集合中的所有元素

判断

6:isEmpty() 判断集合是否为空

7:contains() 判断集合何中是否包含指定对象

            

8:containsAll() 判断集合中是否包含指定集合

                            使用equals()判断两个对象是否相等  

获取:   9:int size()    返回集合
193a0
容器的大小

 

转成数组10: toArray()   集合转换数组

2.2. List

有C基础的可以结合C的数组、栈、链的基础来理解,即数据结构基础。
所属位置:

---| Iterable   接口(迭代器,用于遍历List,不懂的下载本博客最后附带的文档查看例子。)  

     Iterator iterator()   唯一一个方法,返回一个迭代器。

---| Collection     接口

------| List   接口 元素可以重复,允许在指定位置插入元素,并通过索引来访问元素

2.2.1. List集合特有方法

1:增加

        void add(int index, E element) 指定位置添加元素            

        boolean addAll(int index, Collection c) 指定位置添加集合  

2:删除

E remove(int index) 删除指定位置元素

 

3:修改

E set(int index, E element)    返回的是需要替换的集合中的元素

4:查找:

        E get(int index)             注意: IndexOutOfBoundsException

int indexOf(Object o)         // 找不到返回-1

lastIndexOf(Object o)

5:求子集合

 List<E> subList(int fromIndex, int toIndex) // 不包含toIndex   

2.2.2 ArrayList

--| Iterable

  ----| Collection

 ------| List

          ---------| ArrayList  底层采用数组实现,默认10。每次增长 60%,((oldCapacity * 3)/2 + 1) 查询快,增删慢。

     ---------| LinkedList          
ArrayList:实现原理:

数组实现, 查找快, 增删慢

数组为什么是查询快?因为数组的内存空间地址是连续的.

ArrayList底层维护了一个Object[] 用于存储对象,默认数组的长度是10。可以通过 new
ArrayList(20)显式的指定用于存储对象的数组的长度。

当默认的或者指定的容量不够存储对象的时候,容量自动增长为原来的容量的1.5倍。

由于ArrayList是数组实现, 在增和删的时候会牵扯到数组增容, 以及拷贝元素. 所以慢。数组是可以直接按索引查找, 所以查找时较快。

譬如:ArrayList原来是长度是10,由于添加元素,要增大,底层实现就是将其赋值给一个长度为16的新的数组,这个操作是非常慢且繁琐的。

 

2.2.3  LinkedList

--| Iterable

   ----| Collection

 ------| List

             ---------| ArrayList  底层采用数组实现,默认10。每次增长 60%,((oldCapacity * 3)/2 + 1) 查询快,增删慢。

        
  ---------| LinkedList  底层采用链表实现,增删快,查询慢。         
LinkedList:链表实现, 增删快, 查找慢

由于LinkedList:在内存中的地址不连续,需要让上一个元素记住下一个元素.所以每个元素中保存的有下一个元素的位置.虽然也有角标,但是查找的时候,需要从头往下找,显然是没有数组查找快的.但是,链表在插入新元素的时候,只需要让前一个元素记住新元素,让新元素记住下一个元素就可以了.所以插入很快.

由于链表实现, 增加时只要让前一个元素记住自己就可以, 删除时让前一个元素记住后一个元素, 后一个元素记住前一个元素. 这样的增删效率较高。

但查询时需要一个一个的遍历, 所以效率较低。

 

特有方法
1:方法介绍

                addFirst(E e)

addLast(E e)

getFirst()

getLast()

removeFirst()

removeLast()

如果集合中没有元素,获取或者删除元素抛:NoSuchElementException

2:数据结构

1:栈 (1.6)

先进后出

push() 

pop()

2:队列(双端队列1.5)

先进先出

offer()

poll()

3:返回逆序的迭代器对象      

descendingIterator()   返回逆序的迭代器对象

ArrayList 和 LinkedList的存储查找的优缺点对比:

1、ArrayList 是采用动态数组来存储元素的,它允许直接用下标号来直接查找对应的元素。但是,但是插入元素要涉及数组元素移动及内存的操作。总结:查找速度快,插入操作慢。

2、LinkedList 是采用双向链表实现存储,按序号索引数据需要进行前向或后向遍历,但是插入数据时只需要记录本项的前后项即可,所以插入速度较快

问题:有一批数据要存储,要求存储这批数据不能出现重复数据,ArrayList、LinkedList都没法满足需求。解决办法:使用 set集合。

2.2.4 Vector(多线程安全,了解即可。)

Vector: 描述的是一个线程安全的ArrayList。

         

ArrayList: 单线程效率高

Vector   : 多线程安全的,所以效率低
特有的方法:

Vector: 描述的是一个线程安全的ArrayList。
                         void addElement(E obj)  在集合末尾添加元素

                         E elementAt( int index) 返回指定角标的元素

                         Enumeration elements()  返回集合中的所有元素,封装到Enumeration对象中

  

2.3. 迭代器

为了方便的处理集合中的元素,Java中出现了一个对象,该对象提供了一些方法专门处理集合中的元素.例如删除和获取集合中的元素.该对象就叫做迭代器(Iterator).

对 Collection 进行迭代的类,称其为迭代器。还是面向对象的思想,专业对象做专业的事情,迭代器就是专门取出集合元素的对象。但是该对象比较特殊,不能直接创建对象(通过new),该对象是以内部类的形式存在于每个集合类的内部。

如何获取迭代器?Collection接口中定义了获取集合类迭代器的方法(iterator()),所以所有的Collection体系集合都可以获取自身的迭代器。

正是由于每一个容器都有取出元素的功能。这些功能定义都一样,只不过实现的具体方式不同(因为每一个容器的数据结构不一样)所以对共性的取出功能进行了抽取,从而出现了Iterator接口。而每一个容器都在其内部对该接口进行了内部类的实现。也就是将取出方式的细节进行封装。

2.3.1. Iterable

Jdk1.5之后添加的新接口, Collection的父接口. 实现了Iterable的类就是可迭代的.并且支持增强for循环。该接口只有一个方法即获取迭代器的方法iterator()可以获取每个容器自身的迭代器Iterator。(Collection)集合容器都需要获取迭代器(Iterator)于是在5.0后又进行了抽取将获取容器迭代器的iterator()方法放入到了Iterable接口中。Collection接口进程了Iterable,所以Collection体系都具备获取自身迭代器的方法,只不过每个子类集合都进行了重写(因为数据结构不同)

2.3.2. Iterator

Iterator iterator() 返回该集合的迭代器对象
该类主要用于遍历集合对象,该类描述了遍历集合的常见方法

1:java.lang. Itreable  

 ---| Itreable      接口 实现该接口可以使用增强for循环

---| Collection 描述所有集合共性的接口

---| List接口     可以有重复元素的集合

---| Set接口     不可以有重复元素的集合
 

public interface Iterable<T>

Itreable   该接口仅有一个方法,用于返回集合迭代器对象。 1: Iterator<T> iterator() 返回集合的迭代器对象
 (什么是迭代器?http://www.cnblogs.com/airwindow/archive/2012/06/25/2560811.html      这个文章有解释)

 

Iterator接口定义的方法
Itreator 该接口是集合的迭代器接口类,定义了常见的迭代方法

1:boolean hasNext()

判断集合中是否有元素,如果有元素可以迭代,就返回true。

2: E next()  

返回迭代的下一个元素,注意: 如果没有下一个元素时,调用

next元素会抛出NoSuchElementException

 

3: void remove()

从迭代器指向的集合中移除迭代器返回的最后一个元素(可选操

作)。

2.3.2. List特有的迭代器ListIterator

public interface ListIterator extends Iterator

 

ListIterator<E> listIterator()
---| Iterator

hasNext()

next()

remove()

   ------| ListIterator Iterator子接口 List专属的迭代器

                  add(E e)    将指定的元素插入列表(可选操作)。该元素直接插入到 next 返回的下一个元素的前面(如果有)

                  void set(E o)   用指定元素替换 next 或 previous 返回的最后一个元素

                  hasPrevious()    逆向遍历列表,列表迭代器有多个元素,则返回 true。

                  previous()       返回列表中的前一个元素。
Iterator在迭代时,只能对元素进行获取(next())和删除(remove())的操作。 对于 Iterator 的子接口ListIterator 在迭代list 集合时,还可以对元素进行添加 (add(obj)),修改set(obj)的操作。

2.4. Set

Set:注重独一无二的性质,该体系集合可以知道某物是否已近存在于集合中,不会存储重复的元素

用于存储无序(存入和取出的顺序不一定相同)元素,值不能重复。

对象的相等性

引用到堆上同一个对象的两个引用是相等的。如果对两个引用调用hashCode方法,会得到相同的结果,如果对象所属的类没有覆盖Object的hashCode方法的话,hashCode会返回每个对象特有的序号(java是依据对象的内存地址计算出的此序号),所以两个不同的对象的hashCode值是不可能相等的。

如果想要让两个不同的Person对象视为相等的,就必须覆盖Object继下来的hashCode方法和equals方法,因为Object  hashCode方法返回的是该对象的内存地址,所以必须重写hashCode方法,才能保证两个不同的对象具有相同的hashCode,同时也需要两个不同对象比较equals方法会返回true

该集合中没有特有的方法,直接继承自Collection。
---| Itreable      接口 实现该接口可以使用增强for循环

---| Collection 描述所有集合共性的接口

---| List接口     可以有重复元素的集合

                            ---| ArrayList   

                            ---|  LinkedList

---| Set接口     不可以有重复元素的集合
 

2.4.1. HashSet

---| Itreable      接口 实现该接口可以使用增强for循环

---| Collection 描述所有集合共性的接口

---| List接口     可以有重复元素的集合

                            ---| ArrayList   

                            ---|  LinkedList

---| Set接口     不可以有重复元素的集合

                            ---| HashSet  线程不安全,存取速度快。底层是以哈希表实现的。
 

 

HashSet

哈希表边存放的是哈希值。HashSet存储元素的顺序并不是按照存入时的顺序(和List显然不同) 是按照哈希值来存的所以取数据也是按照哈希值取得。

HashSet不存入重复元素的规则.使用hashcode和equals

由于Set集合是不能存入重复元素的集合。那么HashSet也是具备这一特性的。HashSet如何检查重复?HashSet会通过元素的hashcode()和equals方法进行判断元素师否重复。

当你试图把对象加入HashSet时,HashSet会使用对象的hashCode来判断对象加入的位置。同时也会与其他已经加入的对象的hashCode进行比较,如果没有相等的hashCode,HashSet就会假设对象没有重复出现。

简单一句话,如果对象的hashCode值是不同的,那么HashSet会认为对象是不可能相等的。

因此我们自定义类的时候需要重写hashCode,来确保对象具有相同的hashCode值。

如果元素(对象)的hashCode值相同,是不是就无法存入HashSet中了? 当然不是,会继续使用equals 进行比较.如果 equals为true 那么HashSet认为新加入的对象重复了,所以加入失败。如果equals 为false那么HashSet 认为新加入的对象没有重复.新元素可以存入.

总结:

元素的哈希值是通过元素的hashcode方法 来获取的, HashSet首先判断两个元素的哈希值,如果哈希值一样,接着会比较equals方法
如果 equls结果为true ,HashSet就视为同一个元素。如果equals 为false就不是同一个元素。

哈希值相同equals为false的元素是怎么存储呢,就是在同样的哈希值下顺延(可以认为哈希值相同的元素放在一个哈希桶中)。也就是哈希一样的存一列。

hashtable

 


图1:hashCode值不相同的情况

图2:hashCode值相同,但equals不相同的情况。

HashSet:通过hashCode值来确定元素在内存中的位置。一个hashCode位置上可以存放多个元素。

当hashcode() 值相同equals() 返回为true 时,hashset 集合认为这两个元素是相同的元素.只存储一个(重复元素无法放入)。调用原理:先判断hashcode 方法的值,如果相同才会去判断equals 如果不相同,是不会调用equals方法的。

 

 

HashSet到底是如何判断两个元素重复。

通过hashCode方法和equals方法来保证元素的唯一性,add()返回的是boolean类型

判断两个元素是否相同,先要判断元素的hashCode值是否一致,只有在该值一致的情况下,才会判断equals方法,如果存储在HashSet中的两个对象hashCode方法的值相同equals方法返回的结果是true,那么HashSet认为这两个元素是相同元素,只存储一个(重复元素无法存入)。

注意:HashSet集合在判断元素是否相同先判断hashCode方法,如果相同才会判断equals。如果不相同,是不会调用equals方法的。

 

HashSet 和ArrayList集合都有判断元素是否相同的方法,

boolean contains(Object o)

HashSet使用hashCode和equals方法,ArrayList使用了equals方法

2.4.2. TreeSet  

---| Itreable      接口 实现该接口可以使用增强for循环

---| Collection 描述所有集合共性的接口

---| List接口     有序,可以重复,有角标的集合

                            ---| ArrayList   

                            ---|  LinkedList

---| Set接口     无序,不可以重复的集合

                            ---| HashSet  线程不安全,存取速度快。底层是以hash表实现的。

                            ---| TreeSet   红-黑(二叉树)树的数据结构,默认对元素进行自然排序(String)。如果在比较的时候两个对象返回值为0,那么元素重复。
红-黑树

红黑树是一种特定类型的二叉树



红黑树算法的规则: 左小右大。

既然TreeSet可以自然排序,那么TreeSet必定是有排序规则的。

1:让存入的元素自定义比较规则。

2:给TreeSet指定排序规则。

方式一:元素自身具备比较性

元素自身具备比较性,需要元素实现Comparable接口,重写compareTo方法,也就是让元素自身具备比较性,这种方式叫做元素的自然排序也叫做默认排序。

方式二:容器具备比较性

当元素自身不具备比较性,或者自身具备的比较性不是所需要的。那么此时可以让容器自身具备。需要定义一个类实现接口Comparator,重写compare方法,并将该接口的子类实例对象作为参数传递给TreeMap集合的构造方法。

注意:当Comparable比较方式和Comparator比较方式同时存在时,以Comparator的比较方式为主;

注意:在重写compareTo或者compare方法时,必须要明确比较的主要条件相等时要比较次要条件。(假设姓名和年龄一直的人为相同的人,如果想要对人按照年龄的大小来排序,如果年龄相同的人,需要如何处理?不能直接return 0,因为可能姓名不同(年龄相同姓名不同的人是不同的人)。此时就需要进行次要条件判断(需要判断姓名),只有姓名和年龄同时相等的才可以返回0.)

通过return 0来判断唯一性。

 

问题:为什么使用TreeSet存入字符串,字符串默认输出是按升序排列的?因为字符串实现了一个接口,叫做Comparable 接口.字符串重写了该接口的compareTo 方法,所以String对象具备了比较性.那么同样道理,我的自定义元素(例如Person类,Book类)想要存入TreeSet集合,就需要实现该接口,也就是要让自定义对象具备比较性.

存入TreeSet集合中的元素要具备比较性.

比较性要实现Comparable接口,重写该接口的compareTo方法

TreeSet属于Set集合,该集合的元素是不能重复的,TreeSet如何保证元素的唯一性

通过compareTo或者compare方法中的来保证元素的唯一性。

添加的元素必须要实现Comparable接口。当compareTo()函数返回值为0时,说明两个对象相等,此时该对象不会添加进来。

比较器接口
----| Comparable

        compareTo(Object o)     元素自身具备比较性

----| Comparator

        compare( Object o1, Object o2 ) 给容器传入比较器
 

TreeSet集合排序的两种方式:

一,让元素自身具备比较性。

也就是元素需要实现Comparable接口,覆盖compareTo 方法。

这种方式也作为元素的自然排序,也可称为默认排序。

年龄按照搜要条件,年龄相同再比姓名。

二,让容器自身具备比较性,自定义比较器。 

需求:当元素自身不具备比较性,或者元素自身具备的比较性不是所需的。

那么这时只能让容器自身具备。

定义一个类实现Comparator 接口,覆盖compare方法。

并将该接口的子类对象作为参数传递给TreeSet集合的构造函数。

当Comparable比较方式,及Comparator比较方式同时存在,以Comparator

比较方式为主。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: