1. Collection
1.1 概述
Java集合是使程序能够储存和操纵元素不固定的一组数据。所有java集合类都位于java.util包中。
【问】:之前我们需要把多个元素放到一起的时候,使用的是数组,为什么还要提供Java集合类工具呢?
我们通过对比数组和java集合类工具来解释集合类工具的必要性
数组:长度固定 存放任意类型
集合:长度不固定 不能存放基本数据了icing,只能存放对象的引用
注意:如果集合中存放基本类型,一定要将其“装箱”成对应的基本类型包装类。
1.2 继承体系
以上两图我们可以看出Java类有清晰的继承关系,有很多子类接口和实现类。但是,并不是所有的子接口或实现类都是最常用的。
下面我们列举出最常用的几个子接口和实现类:
Collection----->List----->ArrayList类
Collection----->List----->LinkedList类
Collection----->Set----->HaskSet类
Collection----->Set----->SortedSet接口----->TreeSet类
1.3 常用方法
方法 | 描述 |
boolean add(Object o) | 该方法用于向集合里添加一个元素,成功返回true |
voids claer() | 清除集合里的所有元素,长度变为0 |
boolean contains(Object o) | 返回集合里是否包含指定元素 |
boolean containsAll(Collection c) | 返回集合里是否包含集合c里面的所有元素 |
int hashCode() | 返回此Collection的哈希码值 |
boolean isEmpty() | 返回此集合是否为空,当集合长度为0时,返回true |
Iterator iterator() | 返回一个Iterator对象,用于遍历集合里的元素 |
boolean remove(Object o) | 删除集合中指定元素o,当集合中包含了一个或多个元素o时,这些元素将被删除,返回true |
booleanremoveAll(Collection c) | 从集合中删除集合c里包含的所有元素,如果删除了一个或一个以上的元素,返回true |
boolean retainAkk(Collection c) | 从集合中删除不在集合c里包含的元素,如果删除了一个或一个以上的元素,返回true |
int size() | 返回集合中元素的个数 |
Object[] toArray() | 该方法把集合转换成一个数组,所有集合元素变成对应的数组元素 |
1.4 使用方式
//创建集合对象
Collection c1 = new ArrayList();
//判断是否为空(个数是否为0)
System.out.println(c1.isEmpty);
//集合中时不能保存基本类型的,需要转换为包装类才可以
//这里会进行自从装箱为Integer类型,然后发生多态转型为Object类型进行存储
c1.add(123);
c1.add(new Integer(1));
c1.add(new Collection_01());
//个数
System.out.println(c1.size());
System.out.println(c1.isEmpty());
//删除,会调用要删除元素的equals方法,但是Integer覆写了。所以可以把1删除掉
c1.remove(1);
A a = new A("张三");
A a1 = new A("张三");
c1.add(a);
//所以使用集合保存自定义类型的时候要注意,是否要覆写equals方法,定义怎么算相等
c1.remove(a1);
//把集合转换为数组
Object[] arr = c1.toArray();
for (object:arr){
System.out.println(object);
}
//清空集合
c1.clear();
System.out.println(c1.size());
1.5 注意Contains和remove
Contains(数据):判断集合中是否包含某个元素
remove(数据):删除指定元素
两个方法底层都会自动调用该对象的equals方法,因为不管是判断是否包含还是删除,都哟啊找到这个数据。而寻找数据只能比较,但是集合中保存的都是引用类型,所以比较只能使用equals方法(==比较的是地址,一定找不到,所以只能用equals方法)
所以如果存储的是自定义类型,就要考虑equals方法的覆写问题
2.Iterator
2.1 概述
在面向对象编程里,迭代器模式是一种设计模式,是一种最简单也最常见的设计模式。
它可以让用户透过特定的接口巡访容器中的每一个元素而不用了解底层的实现。
获取该集合的迭代器对象
Iterator it = 集合对象.iterator();
三个方法 :
1 boolean hasNext() : 判断下面还有没有元素,如果有就返回true,没有就返回false
2 E next() : 获取下一个元素,并指向下一个元素
3 remove() : 删除当前指向的元素
三个方法的使用步骤 , 就是 1,2,3 按照这个顺序调用
注意 : 迭代器一旦创建,集合中就不能添加和删除元素(长度不能更改了)
如果添加或者删除了元素,那么迭代器必须重新生成
增强for循环 就是为了让用iterator循环访问的方式简单,写起来更方便,当然功能不太全,比如删除,还是要用iterator来删除。
【for与iterator的对比】
1. Iterator的好处在于可以使用相同的方式去遍历集合中的元素,而不用考虑类的内部实现。
2.使用Iterator来遍历集合中的元素,如果不使用List转而使用Set来组织数据,则遍历元素的代码不用做任何修改
3. 使用for来遍历,那所有遍历此集合的算法都得做相应的调整,因为List有序而Set无序,他们的访问算法也不一样。
4.for循环需要下标
2.2 常用方法
方法 | 描述 |
Boolean hasNext() | 如果被迭代的集合有下一个元素,则返回true |
Object next() | 返回集合里的下一个元素 |
void remove | 删除集合里上一次next方法返回的元素 |
2.3 使用方式
// Collection c1 = new ArrayList();
// Collection c1 = new HashSet();
Collection c1 = new LinkedList();
c1.add(1);
c1.add("abc");
// 1 创建迭代器
Iterator it = c1.iterator();
// 遍历判断下面是否有元素
while (it.hasNext()) {
// 获取并指向下一个元素
Object obj = it.next();
System.out.println(obj);
}
// 使用完之后,想再次使用,需要重新创建
it = c1.iterator();
// 迭代器创建之后,不能添加和删除 , 必须重新生成迭代器
c1.add(2);
c1.add(3);
c1.remove(1);
it = c1.iterator();
while (it.hasNext()) {
// 获取并指向下一个元素
Object obj = it.next();
System.out.println(obj);
// 使用迭代器的时候,不能更改集合个数,所以删除数据的时候不能使用集合的删除,应该使用迭代器的删除
// c1.remove(obj);
it.remove();
}
System.out.println(c1.size() + "----");
3. List
3.1 概述List
特点 : 有序 可重复
有序 : 添加顺序和取出顺序是一致的
可重复 : 就是可以有重复的数据
ArrayList : 底层是个Object数组 , 随机查询和更改效率高, 随机添加和删除 效率低
LinkedList : 底层是一个双向链表,随机查询更改效率低,随机添加和删除效率高
3.2 ArrayList
ArrayList底层是数组,下标从0开始
默认初始化容量为10,扩大容量为原始容量的1.5倍
并且默认容量是第一次添加数据的时候设置的
也就是说 我们 new ArrayList() 的时候,数组长度是为0的
1.5倍 : 长度 + 长度>>1
3.2.1 基本使用
public static void main(String[] args) {
// 创建对象
// Vector 已经过时了,ArrayList是Vector的升级版,Vector是线程安全,而ArrayList是非线程安全的
List list = new ArrayList();
list.add(100);
list.add(123);
// [100, 123] 覆写了toString方法
System.out.println(list);
}
3.2.2 常用方法
package com;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class Collection_04_List_02 {
public static void main(String[] args) {
List list = new ArrayList();
list.add(1);
list.add(2);
list.add(3);
// add(E e ) : 尾部添加
// add(int index , E e ) : 添加到指定位置
// set(int index, E e ) : 更改指定位置上的元素值
// remove(Object object) : 根据元素内容删除
// remove(int index) : 根据索引删除
// get(int index) : 获取指定位置的数据
list.add(1, 4);
list.set(3, 33);
System.out.println(list.get(2));
// 这是根据索引删除
list.remove(1);
// 删除元素值为1
list.remove(new Integer(1));
System.out.println(list);
// 传统for遍历
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}
// foreach遍历
for (Object object : list) {
System.out.println(object);
}
// 迭代器遍历
Iterator it = list.iterator();
while (it.hasNext()) {
System.out.println(it.next());
}
}
}
3.2.3 遍历
// 传统for遍历
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}
// foreach遍历
for (Object object : list) {
System.out.println(object);
}
// 迭代器遍历
Iterator it = list.iterator();
while (it.hasNext()) {
System.out.println(it.next());
}
3.3 LinkedList
3.3.1 基本使用和方法
总结:和ArrayList操作一模一样
LinkedList list = new LinkedList();
// add(E e ) : 尾部添加
// add(int index , E e ) : 添加到指定位置
// set(int index, E e ) : 更改指定位置上的元素值
// remove(Object object) : 根据元素内容删除
// remove(int index) : 根据索引删除
// get(int index) : 获取指定位置的数据
// 首部添加
list.addFirst(1);
// 首部添加
list.push(11);
// 首部添加 成功返回true
list.offerFirst(111);
// 尾部添加
list.addLast(2);
// 尾部添加 成功返回true
list.offerLast(22);
// 尾部添加 成功返回true
list.offer(222);
// 上面这几个方法 没啥区别,本质就是 linkLast 和 linkFirst
list.add(1);
list.add(2);
list.add(3);
list.add(0, 4);
list.set(3, 33);
System.out.println(list.get(2));
// 这是根据索引删除
list.remove(1);
// 删除元素值为1
list.remove(new Integer(1));
System.out.println(list);
// 传统for遍历
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}
// foreach遍历
for (Object object : list) {
System.out.println(object);
}
// 迭代器遍历
Iterator it = list.iterator();
while (it.hasNext()) {
System.out.println(it.next());
}
3.3.2 底层实现
3.3.2.1 Node节点类
private static class Node<E>{
E item next;
Node<E> next;
Node<E> prev;
Node (Node<E>)prev,E element,Node<E>next){
this.item = element;
this.next = next;
this.prev = prev;
}
链表:保存的都是节点,一个节点有三个属性,添加的数据,上一个节点的引用,下一个节点的引用。
3.3.2.2 LinkedList类
public class LinkedList<E>
extend AbstractSequentialList<E>
implements List<E>,Deque<E>,Cloneable,java.io.Serializable{
transient int size = 0;//添加首个元素个数
transient Node<E>first;//首节点
raansient Node<E>last;//尾节点
3.3.2.3 add方法的实现
void linkLast(E e){
final Node<E> I = last;
final Node<E> newNode = new Node<>(l,e,null);
last = newNode;
if(I == null){
first = newNode;
}else{
I.next = newNode;
size++;
modCount++;
}
3.3.2.4 Get方法的实现
public E get(int index){
checkElementIndex(index);//校验下标
return node(index).item;//获取数据
}
Get方法只是模拟了下标获取数据的方式,本质就是遍历,从头挨个去找
所以这可以理解为 并不是下标,只是决定了循环次数,而 ArrayList的get才是根据下标获取。