数组 VS 集合
数组:长度固定;
集合:长度不固定
集合特点:
1. 用于存储对象
2. 集合长度可变
3. 可存储不同类型对象
集合继承关系
集合容器的创建及使用
步骤:
1. 导入文件 import java.util.*
2. 创建一个容器,使用Collection接口的子类 .ArrayList;
ArrayList al = new ArrayList();
3. 加入对象
a1.add(“java1”);
常用方法:
增加元素:
boolean | |
boolean | addAll(Collection<? extendsE> c) |
注:集合中存储的都是对象的引用。
删除元素:
void | clear() |
boolean | |
boolean | removeAll(Collection<?> c) |
判断元素:
| |
boolean | |
boolean | containsAll(Collection<?> c) |
boolean | isEmpty() |
获取相关数据:
int | size() | |
Object[] | toArray() | |
| toArray(T[] a) | |
iterator() |
获取两级和的交集
boolean | retainAll(Collection<?> c) |
取出元素:
同过迭代器对数据进行取出。
it.next();在取完值之后将指针移到下一个数据。得到的是Object类型的数据。
迭代器
概述:为了方便地访问集合内容的元素,我们把取出方式定义在集合内部。取出方式就被定义为内部类。
每一个容器的数据结构不同,所以取出的动作细节不一样,但是都有共性的内容判断和取出。那么可以写共性抽取从而形成具备此功能的类Iterator。并通过一个对外提供的方法:.iterator()获得多内部数据操作的一个工具。
迭代器方法:
1、 获取下一个数据
2、 判断是否还有下一个数据
3、 删除某个数据
Collection类的两个子类:
1、 List:元素都是有序的,元素可以重复。
2、 Set:元素都是无序的,元素不可以重复。
List结合容器
List容器特有方法:(凡可操纵脚标的方法都为其特有方法)
增:
void | |
boolean | addAll(int index,Collection<? extendsE> c) |
删:
remove(int index) |
改
查
get(int index) | |
subList(int fromIndex, int toIndex) | |
listIterator(int index) | |
int |
集合容器并发访问错误:
不能对同一元素进行不同方式的操作:
Eg:Iterator it = a1.iterator(); // 并发访问错误。
a1.add(“java001”);
分析:既用了迭代器有用到了原对象,从而引发并发修改异常。
并发访问错误的原因:
Iterator 是工作在一个独立的线程中,并且拥有一个 mutex 锁。 Iterator 被创建之后会建立一个指向原来对象的单链索引表,当原来的对象数量发生变化时,这个索引表的内容不会同步改变,所以当索引指针往后移动的时候就找不到要迭代的对象,所以按照 fail-fast 原则 Iterator 会马上抛出java.util.ConcurrentModificationException 异常。
Iterator迭代器的操作方法:
boolean | hasNext() |
next() | |
void | remove() |
List集合容器迭代器ListIterator的特点:
特点:因为List有脚标,所以其持有的迭代器可以对元素进行操作。
在使用迭代器时,不能通过集合对象方法操作集合中的元素。可是iterator方法有限,只能对元素进行判断,取出,删除操作。若要想进行其他操作eg添加、修改。就需要使用其子接口:ListIterator;
其特有的方法:
void | |
boolean | hasPrevious() |
int | nextIndex() |
previous() | |
int | previousIndex() |
void |
List的子派系:
ArrayList:底层数据结构采用的是数组结构(查询块,增删慢)
LinkList:底层数据结构使用的是链式结构(查询慢,增删块)
Vector:底层是数组结构。(和ArrayList功能一致,只是Vector出现于JDK1.0,而那时集合还未出现,因此在如今,其已被ArrayList取代。)
ArrayList “vs” Vector
1、 ArrayList线程不同步,Vector线程同步(ArrayList 替代了 Vector)
2、 ArrayList开始长度为10,以50%延长;Vector开始长度为10,以100%延长。
Vector的特别之处:利用枚举进行取数(类似迭代器)
Vector取数方式:
1、 增:
| addElement |
| insertElementAt |
2、 删
| removeAllElements |
| removeElement |
| removeElementAt |
3、 改
| setElementAt |
4、 查
firstElement |
5、 迭代器
elements |
6、 遍历
Vector的枚举变量:Enumeration
| hasMoreElements |
nextElement |
Vector的枚举仅有两种方法。与迭代器很类似。(其实他们方法一致,因此在升级后被迭代器锁取代)
注:有Element的操作都为Vector的特有操作。
LinkedList集合容器
LinkList特有方法:
增
void | |
void |
JDK1.6后出现的新方法:
boolean | offerFirst(E e) |
boolean |
删
removeFirst() | |
removeLast() |
若没有元素,会产生异常NoSuchElementException;
JDK1.6后出现的新方法:
pollFirst() | |
pollLast() |
查
getFirst() | |
getLast() |
若没有元素,会产生异常NoSuchElementException
JDK1.6后出现的新方法:
peekFirst() | |
peekLast() |
通过LinkedList实现队列:
实例:
在39行,判断数据是否存在时,会调用到Person对象的比较方法equals。为此,我们需要在Person中复写比较方法,以保证比较方式为我们所期望的方式。删除方法也依赖equals方法。
Set集合容器
特点:其中存储的是无序、不可以重复元素。
其方法和Collection一致。
HashSet:
底层数据结构是哈希表,线程非同步;
特点:
(保证元素唯一性的方法:先通过判断哈希code值,若相同则继续判断equals方法)
因此,为了保证两个方法都能正常使用,我们需要依据情况复写为我们需要的方法。
在求哈希值时,应该尽量保证哈希值的唯一性。
只要是涉及到元素比较的方法时,对于HashSet来说一定要用到hashCode()和equals()方法。
注意:
常犯的错误:1、equals的参数为Object,为了能复写equals方法必须保证参数一致
2、hashCode很容易写错。
存储结构:
Eg:Person类;
P1 = Person(”zhansan1”,49),哈希值:66;
P2 = Person(”zhansan2”,45),哈希值:66;
P3 = Person(”zhansan1”,49),哈希值:66;
P4 = Person(”zhansan4”,33),哈希值:56;
P5 = Person(”zhansan5”,45),哈希值:44;
P5 | P4 | P1 |
|
|
|
| P2 |
|
|
|
|
|
|
|
|
|
|
|
|
P3因为哈希值和通过equals方法和p1的都一样,因此被判断为同一个数据并被移除。
TreeSet:
可以对Set集合中的元素进行排序。底层数据结构为二叉树。
特点:
(TreeSet通过CompareTo()实现排序,在排序时首先比较主要内容,再比较较次要内容(主要通过二叉树进行存储)。通过CompareTo()的返回值进行操作,从而确定输出。)
TreeSet排序方式:
方式一:让元素自身具备比较性。
元素需要实现Compareable接口,复写compareTo方法。这种方式也成为元素的自然顺序,或者叫做默认顺序。
当年龄相同时,我们就需要比较其他数据来确保排序。(TreeSet不能有同一元素)
对于一般的基本数据类型,有自己的compareTo方法。
方式二:让容器自身具备比较性
当元素不具备比较性,或者具备的比较性不是所需要的时,这时就让集合本身具备比较性。在集合初始化时就给他附一个比较器,以使其在初始化就有比较性。
TreeSet(
Comparator<? super
E> comparator)
构造一个新的空TreeSet,它根据指定比较器进行排序。
注:当两种排序都存在时,以比较器为主。
练习:
字符串通过长度进行排序: