集合
1.概述:
(1)为什么出现集合类?
面向对象语言对事物的体现都是以对象的形式,所以为了方便对多个对象的操作,Java就提供了集合类。
(2)数组和集合类同是容器,有何不同?
数组虽然也可以存储对象,但长度是固定的;
集合长度是可变的。数组中可以存储基本数据类型,集合只能存储对象。
(3)集合类的特点
集合只用于存储对象,集合长度是可变的,集合可以存储不同类型的对象。
二. 继承体系
我们主要学习集合中的两大继承体系
三.Collection接口
1、引入:
从API中我们可以了解到Collection是一个接口,其中只有抽象方法,接口无法实例化,所以我们要找该接口的实现子类,借助这个子类学习使用Collection中的方法:
从上述的继承关系中我们可以得知:
Collection下面又有两大子接口:
- List接口:
- 实现子类: ArrayList
- Set接口
2、Collection接口成员方法
前言
(1)ArrayList是实现了List接口的具体子类,List接口是继承自Collection接口,我们可以借助创建ArrayList对象,来使用学习Collection接口中的方法
(2)ArrayList类中有一个无参构造方法,可以让我们创建一个空的集合
注意:
java中的集合只能存储引用数据类型,即使传入的是基本数据类型,底层也会讲基本类型的数据转化为对应的包装类的类型传入集合。
2. Collection接口成员方法:
下列参数列表中的E可以理解为Object(元素)
boolean add(E e) //向集合中添加元素 可以存放任意的引用数据类型
boolean remove(Object o) //删除一个元素
void clear() //清空集合
boolean contains(Object o) // 判断集合是否包含某个元素
boolean isEmpty() int size() //获取集合中的元素个数
boolean addAll(Collection c) //将一个集合中的元素添加到另一个集合中
c1.removeAll(c2) // 在c1中移除与c2相同部分的元素
boolean containsAll(Collection c) // 判断一个集合中是否包含另一个集合的所有元素
boolean retainAll(Collection c) //求交集
演示:
import java.util.ArrayList;
import java.util.Collection;
/*
回顾一下到目前为止,我们学习过哪些容器
数组:长度一旦确定就不能更改,对于同一个数组中,既可以是存放基本数据类型元素的数组,也可以是存放引用数据类型数组
StringBuffer:无论存放什么类型的元素,一旦进入到StringBuffer,都是以字符类型的形式存储,长度可以改变
集合:结合了数组和长度可变的优点
集合是一个统称,并不是指某一种类。
属于的集合既然是一个容器,那么这些容器都有一些相同的共性。
我们学习,本质上是学习不同的集合类,这些集合类之间有相同的属性或者行为,集合与集合之间会形成继承体系。
比如有些集合不允许元素重复,有些集合允许元素重复,有些集合可以对元素进行排序,有些集合对查找有更好的支持....
换句话说,就是不同的集合底层的数据结构不同,导致了不同的集合具有不同的特点。
通过观察发现Collection是一个接口,其中只有抽象方法,接口无法实例化,所以我们要找该接口的实现子类,借助这个子类学习使用Collection中的方法
Collection下面又有两大子接口
- List接口
- 实现子类:ArrayList
- Set接口
ArrayList是实现了List接口的具体子类,List接口是继承自Collection接口
借助创建ArrayList对象,来使用学习Collection接口中的方法
ArrayList类中有一个无参构造方法,可以让我们创建一个空的集合
Collection中的抽象方法一定会在具体子类ArrayList中找到实现:
boolean add(Object e)
boolean remove(Object o)
void clear()
boolean contains(Object o)
boolean isEmpty()
int size()
boolean addAll(Collection c) 将一个集合中的元素添加到另一个集合中
c1.removeAll(c2); 在c1中移除与c2相同部分的元素
boolean containsAll(Collection c) 判断一个集合中是否包含另一个集合的所有元素
boolean retainAll(Collection c) 求交集
注意:
1、java中的集合只能存储引用数据类型
2、
*/
public class CollectionDemo1 {
public static void main(String[] args) {
Collection c1 = new ArrayList();
//boolean add(Object e) 向集合中添加元素 可以存放任意的引用数据类型
c1.add(22); // 这里底层涉及到今天说的自动装箱 int:22 --> Integer
/*
ArrayList extends AbstractList
AbstractList extends AbstractCollection
*/
c1.add("java");
c1.add("python");
c1.add("mysql");
c1.add("hadoop");
c1.add(false);
c1.add(false);
// toString()底层实际使用的是ArrayList的父类的父类AbstractCollection中的toString()
System.out.println(c1.toString()); //[22, java, python, mysql, hadoop, false, false]
//boolean remove(Object o) 删除
Object c2 = c1.remove(false); // 调用一次,只会删一次值
System.out.println(c2); //true c2返回的结果是该元素是否被删除,不是删除的元素值
System.out.println(c1); //[22, java, python, mysql, hadoop, false]
System.out.println("+++++++++++++++++++++++++++++++");
//boolean contains(Object o) 判断集合是否包含某个元素
Object c3 = c1.contains("java");
Object c4 = c1.contains(true);
System.out.println(c3); //true
System.out.println(c4); //false
//boolean isEmpty() //判断集合是否为空
System.out.println(c1.isEmpty()); //false
//int size() 获取集合中的元素个数
System.out.println(c1.size()); //6
//boolean addAll(Collection c) 将一个集合中的元素添加到另一个集合中
Collection cc2 = new ArrayList();
boolean b = cc2.addAll(c1);
System.out.println(b); //true
System.out.println(cc2); //[22, java, python, mysql, hadoop, false]
// c1.removeAll(c2); //在c1中移除与cc2相同部分的元素
System.out.println("==============================================");
cc2.remove("hadoop");
c1.add("xusong");
System.out.println(cc2);
System.out.println(c1);
// cc2.removeAll(cc2);
System.out.println("移除后的结果:");
System.out.println("cc2:"+cc2); //[]
System.out.println("c1:"+c1); //[22, java, python, mysql, hadoop, false, xusong]
// boolean containsAll(Collection c) 判断一个集合中是否包含另一个集合的所有元素
boolean res1 = cc2.containsAll(c1);
System.out.println(res1); //false
//boolean retainAll(Collection c) 求交集
c1.retainAll(cc2); // c1与c2做交集 交集的结果存储在c1中,c2不变
System.out.println("c1"+c1);
System.out.println("cc2"+cc2);
}
}
3.集合的遍历
方式1:Object[] toArray() 将集合转成数组,遍历数组,得到集合中每个元素
方式2: Iterator iterator() 迭代器,集合的专用遍历方式
演示:
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
/*
如何遍历一个集合:
方式1:
Object[] toArray()
把集合转成数组,可以实现集合的遍历
方式二:
Iterator iterator()
迭代器,集合的专用遍历方式
学习集合的4步骤:
1、创建集合对象
2、创建元素对象
3、向集合中添加元素
4、遍历集合
*/
public class CollectionDemo2 {
public static void main(String[] args) {
// 1、创建集合对象
Collection c1 = new ArrayList();
// 2、创建元素对象,向集合中添加元素
c1.add("java");
c1.add("hadoop");
c1.add("python");
c1.add("mysql");
c1.add("hadooop");
//3、遍历集合
//方式1: // Object[] toArray() 将集合转成数组,遍历数组,得到集合中每个元素
Object[] arr = c1.toArray();
for (int i = 0; i < arr.length; i++) {
String s = (String) arr[i];
System.out.println(s + " " + s.length());
}
//方式二: Iterator iterator()
//迭代器,集合的专用遍历方式
//Iterator iterator() 迭代器遍历 是Collection集合特有遍历方式 可遍历的序列
//获取迭代器对象,其中存储了集合元素
//因为这里的c1实际上堆内存是ArrayList的对象,所以调用iterator()底层返回的是一个ArrayList类中的私有成员内部类Itr的对象
Iterator iterator = c1.iterator(); // //Iterator iterator = new Itr()
/*
/*
public Iterator<E> iterator() {
return new Itr();
}
*/
//我们应该在取下一个元素之前,先判断一下,下一个位置上有没有元素,如果有,就next(),否则就不获取
while (iterator.hasNext()) { //hasnext()判断下一个位置否有元素存在
String s = (String) iterator.next(); //next()获取下一个元素值并将迭代器的指针往后移一位(指针最开始在迭代器的最左段且不指向元素值)
System.out.println( s + " " + s.length());
}
}
}
Collection案例:
题目:存储自定义对象并遍历 Student(name,age)
/*
题目:存储自定义对象并遍历Student(name,age)
*/
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
class Student {
String name;
int age;
public Student() {
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
public class CollectionTest {
public static void main(String[] args) {
Student student1 = new Student("小1", 20);
Student student2 = new Student("小2", 10);
Student student3 = new Student("小3", 30);
Student student4 = new Student("小4", 40);
Collection c1 = new ArrayList();
c1.add(student1);
c1.add(student2);
c1.add(student3);
c1.add(student4);
Iterator iterator = c1.iterator();
while (iterator.hasNext()) {
Student student = (Student) (iterator.next());
System.out.println(student.toString());
}
}
}
在学习之后的内容我们需要了解一些基本的数据结构,以便更能理解之后的内容
四、基本数据结构及其特点
1.栈和队列
2.数组和链表
3.哈希表
4.树
五.List接口
1.List接口概述
有序的 collection(也称为序列),此接口的用户可以对列表中每个元素的插入位置进行精确地控制。用户可以根据元素的整数索引(在列表中的位置)访问元素,并搜索列表中的元素,与 set 不同,列表通常允许重复的元素。
2.List接口成员方法
void add(int index,Object element) 指定索引位置添加元素
E remove(int index) 指定索引删除元素,返回被删除的元素值
Object get(int index) 指定索引获取索引位置上的元素,返回该元素值
Object o1 = list1.set(4, "datax"); 通过索引设置元素值,返回原始的元素
ListIterator listIterator() 列表迭代器,List集合特有的迭代器
boolean hasPrevious() 与hasnext()相反,查询前一个元素
E previous() 与next()相反,获取前一个元素并将指针往前移一位
演示:
/*
Collection(接口):
- List 元素有序且允许重复,集合具有索引的概念(接口)
- ArrayList(具体子类)
- Set 元素无序且唯一,集合没有索引
因为List相关集合具备了索引的概念,所以List集合它里面就会出现与索引相关的方法
List集合中的元素具有有序的特点,这里的有序不是指排序,而是指的是存储和取出的顺序一致。
List的成员方法:
//void add(int index,Object element) 指定索引位置添加元素
//E remove(int index) 指定索引删除元素,返回被删除的元素值
//Object get(int index) 指定索引获取索引位置上的元素,返回该元素值
//Object o1 = list1.set(4, "datax"); //通过索引设置元素值,返回原始的元素
//ListIterator listIterator() 列表迭代器,List集合特有的迭代器
*/
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
public class ListDemo1 {
public static void main(String[] args) {
List list1 = new ArrayList();
list1.add("于禁");
list1.add("曹操");
list1.add("许褚");
list1.add("张辽");
list1.add("典韦 ");
//void add(int index,Object element) 指定索引位置添加元素
list1.add(2,"徐晃");
System.out.println(list1);
//E remove(int index) 指定索引删除元素,返回被删除的元素值
Object res1 = list1.remove(2);
System.out.println(res1); //徐晃
System.out.println(list1);
//Object get(int index) 指定索引获取索引位置上的元素,返回该元素值
Object res2 = list1.get(3);
System.out.println(res2); //张辽
//Object o1 = list1.set(4, "datax"); //通过索引设置元素值,返回原始的元素
Object res3 = list1.set(1, "曹纯");
System.out.println(res3); //曹操
System.out.println(list1);
//ListIterator listIterator() 列表迭代器,List集合特有的迭代器
ListIterator listIterator = list1.listIterator();
while (listIterator.hasNext()){
System.out.println(listIterator.next());
}
System.out.println("=====================================================");
//无论是ListIterator迭代器,还是Iterator迭代器,指针只有一个,而且最开始的时候,在迭代器的最左边
//所以要想反向遍历,得先正向遍历一遍,将指针移动到最右边,
while (listIterator.hasPrevious()){
System.out.println(listIterator.previous());
}
}
}
3.练习:(易错)
题目:在一个存储字符串的集合中,查找"java"这个元素,如果找到了,就将"flink"添加到集合中,可以添加在任意位置。
此练习主要为了说明Iterator迭代器在迭代过程中可能出现的问题,例如;
ConcurrentModificationException (报错) 并发修改异常
可以理解为此报错主要原因是集合在增删元素时与迭代器的长度不一致导致报错
如何避免这样的问题:迭代器遍历,迭代器修改,集合遍历,集合修改
演示:
/*
在一个存储字符串的集合中,查找"java"这个元素,如果找到了,就将"flink"添加到集合中,可以是任意位置
ConcurrentModificationException 并发修改异常
原因是,在遍历迭代器的时候,集合的长度发生了变化,而迭代器不知道,长度不一致,报错。
如何避免这样的问题。
迭代器遍历,迭代器修改,集合遍历,集合修改
*/
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
public class ListDemo2 {
public static void main(String[] args) {
List list1 =new ArrayList();
list1.add("于禁");
list1.add("曹操");
list1.add("许褚");
list1.add("张辽");
list1.add("java");
list1.add("典韦 ");
//方式一:转数组
Object[] array = list1.toArray();
for (int i = 0; i < array.length; i++) {
String s = (String)array[i];
if ("java".equals(s)){
list1.add("flink");
}
}
System.out.println(list1); //[于禁, 曹操, 许褚, 张辽, java, 典韦 , flink]
System.out.println("===========================================");
//方式二: ListIterator遍历
list1.remove("flink");
ListIterator listIterator = list1.listIterator();
while (listIterator.hasNext()){
String s1 = (String)listIterator.next();
if ("java".equals(s1)){
listIterator.add("flink");
}
}
System.out.println(list1); //[于禁, 曹操, 许褚, 张辽, java, flink, 典韦 ]
方式3(错误):Iterator遍历
// list1.remove("flink");
// Iterator iterator = list1.iterator();
// while (iterator.hasNext()){
// String s2 = (String)iterator.next();
// if ("java".equals(s2)){
// list1.add("flink"); // ConcurrentModificationException 并发修改异常
// }
// }
// System.out.println(list1);
}
}
结果:
(一)Vector类
1.概述:
底层数据结构是数组,查询快,增删慢 线程安全,效率低
2.构造方法:
Vector() 构造一个空向量,使其内部数据数组的大小为 10 ,标准容量增量为零。
3.Vector类特有功能
public void addElement(E obj) 添加元素,可以使用add进行代替
public E elementAt(int index) 根据索引获取元素,使用get进行代替
public Enumeration elements() 获取所有的元素 // 这个方式可以使用迭代器代替遍历
演示:
/*
public void addElement(E obj) 添加元素,可以使用add进行代替
public E elementAt(int index) 根据索引获取元素,使用get进行代替
public Enumeration elements() 获取所有的元素 这个方式可以使用迭代器代替遍历
*/
import java.util.Enumeration;
import java.util.Vector;
/*
Collection(接口)
- List(接口)
- ArrayList(实现类) 底层数据结构是数组,查询快,增删慢,线程是不安全的,效率高。
- Vector(实现类) 底层数据结构是数组,查询快,增删慢,线程是安全的,效率低。(即使这个是线程安全的,我们今后也不用,后面我们会将不安全的ArrayList变成安全的)
-
- Set(接口)
*/
public class VectorDemo {
public static void main(String[] args) {
//构造方法
//Vector()
//构造一个空向量,使其内部数据数组的大小为 10 ,标准容量增量为零。
//创建一个Vector对象
Vector vector = new Vector();
vector.addElement("断桥残雪");
vector.addElement("千百度");
vector.addElement("有何不可");
vector.addElement("雅俗共赏");
vector.addElement("拆东墙");
Object res = vector.elementAt(2);
System.out.println(res); //有何不可
Enumeration elements = vector.elements();
while (elements.hasMoreElements()){
System.out.println(elements.nextElement());
}
}
}
(二)LinkedList类
1.概述
底层数据结构是链表,查询慢,增删快 线程不安全,效率高
2.构造方法
LinkedList() 构造一个空列表
3.LinkedList类特有功能
public void addFirst(E e)及addLast(E e ) 指定开头,末尾添加元素
public E getFirst()及getLast() 获取开头,末尾元素
public E removeFirst()及public E removeLast()
演示:
/*
Collection(接口)
- List(接口)
- ArrayList(实现类) 底层数据结构是数组,查询快,增删慢,线程是不安全的,效率高。
- Vector(实现类) 底层数据结构是数组,查询快,增删慢,线程是安全的,效率低。(即使这个是线程安全的,我们今后也不用,后面我们会将不安全的ArrayList变成安全的)
- LinkedList(实现类) 底层数据结构是双链表,增删快,查询慢,线程不安全的,效率高。
- Set(接口)
LinkedList类特有功能:
public void addFirst(E e)及addLast(E e )
public E getFirst()及getLast()
public E removeFirst()及public E removeLast()
*/
import java.util.LinkedList;
public class LinkedListDemo1 {
public static void main(String[] args) {
LinkedList linkedList = new LinkedList();
linkedList.addLast("降温");
linkedList.addLast("合拍");
linkedList.addFirst("乌鸦");
linkedList.addFirst("庞贝");
System.out.println(linkedList); //[庞贝, 乌鸦, 降温, 合拍]
Object first = linkedList.getFirst();
System.out.println(first); //庞贝
Object last = linkedList.getLast();
System.out.println(last); //合拍
System.out.println("===================================");
System.out.println("删除前:"+linkedList);
Object re_first = linkedList.removeFirst();
Object re_last = linkedList.removeLast();
System.out.println(re_first); //庞贝
System.out.println(re_last); //合拍
System.out.println("删除后:"+linkedList); //[乌鸦, 降温]
}
}
(三)总结
Collection(接口):
List(接口):
ArrayList(实现类) :底层数据结构是数组,查询快,增删慢,线程是不安全的,效率高。
Vector(实现类): 底层数据结构是数组,查询快,增删慢,线程是安全的,效率低。(即使这个是线程安全的,我们今后也不用,后面我们会将不安全的ArrayList变成安全的)
LinkedList(实现类) :底层数据结构是双链表,增删快,查询慢,线程不安全的,效率高。
(四)List集合练习
ArrayList
1.去除集合中字符串的重复值(字符串的内容相同)
import java.util.ArrayList;
import java.util.ListIterator;
/*
ArrayList
去除集合中字符串的重复值(字符串的内容相同)
*/
public class ListTest1 {
public static void main(String[] args) {
ArrayList list1 = new ArrayList();
list1.add("java");
list1.add("python");
list1.add("java");
list1.add("hadoop");
list1.add("flink");
// System.out.println(list1);
//新建一个空集合来存储不重复的元素
ArrayList list2 = new ArrayList();
//遍历旧集合,将不重复的元素添加到新集合中
ListIterator listIterator1 = list1.listIterator();
while (listIterator1.hasNext()) {
String s = (String) listIterator1.next();
if (!list2.contains(s)){
list2.add(s);
}
}
System.out.println(list2);
}
}
2.去除集合中自定义对象的重复值(对象的成员变量值都相同)
/*
使用ArrayList去除集合中自定义对象的重复值(对象的成员变量值都相同)
按照上一个去重字符串的逻辑对对象进行去重发现,并没有去重
经过简单的分析后发现,if (!list2.contains(t))永远为true,元素才会不断的向新集合中添加
!list2.contains(t)整体永远为true, 意味着list2.contains(t)永远为false
现在应该去看contains()方法的源码
public boolean contains(Object o) {
return indexOf(o) >= 0; // indexOf(o)永远为-1 indexOf(o) >= 0 永远为false
}
public int indexOf(Object o) {
if (o == null) {
for (int i = 0; i < size; i++)
if (elementData[i]==null)
return i;
} else {
for (int i = 0; i < size; i++)
if (o.equals(elementData[i]))
return i;
}
return -1;
}
通过分析后发现,底层判断元素是否重复,是调用了元素类型中的equals方法来比较的,而我们Teacher类中没有重写equals方法
调用的是父亲Object类中的equals方法,而Object类中的equals比较的是地址值,我们的每一个Teacher对象都是new出来的,地址值肯定不一样
所以equals比较的结果永远为false
要想达到去重效果,就应该比较对象的成员变量值,而不是比较地址值,我们应该是当对象的成员变量值相同的时候,认为是同一个对象
只需要元素的类型中重写equals方法即可,自动生成即可。
*/
import java.util.*;
class Student1{
String name;
int age;
public Student1() {
}
public Student1(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() { //重写toString(),否则打印的为地址值
return "Student1{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
public boolean equals(Object o) { //重写equals(),否则contains底层比较的为地址值
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student1 student1 = (Student1) o;
return age == student1.age && Objects.equals(name, student1.name);
}
}
public class ListTest2 {
public static void main(String[] args) {
Student1 student1 = new Student1("小五",7);
Student1 student2 = new Student1("小六",8);
Student1 student3 = new Student1("小七",9);
Student1 student4 = new Student1("小九",19);
Student1 student5 = new Student1("小五",7);
ArrayList list3 = new ArrayList();
list3.add(student1);
list3.add(student2);
list3.add(student3);
list3.add(student4);
list3.add(student5);
ArrayList list4 = new ArrayList(); //创建新集合
Iterator iterator = list3.iterator();
while (iterator.hasNext()) {
Object o = iterator.next();
Student1 t = (Student1) o;
if (!list4.contains(t)) {
list4.add(t);
}
}
System.out.println("list4: " + list4);
System.out.println("list3: " + list3);
}
}
LinkedList
3.请用LinkedList模拟栈数据结构的集合,并测试
/*
LinkedList
请用LinkedList模拟栈数据结构的集合,并测试
栈:先进后出
如果笔试的时候,直接使用LinkedList对象存储元素的话,0分
题目的本意是,自己写一个类,类中将LinkedList进行封装,使用自己的类创建对象,底层用的是LinkedList
*/
import java.util.LinkedList;
class Stack {
private LinkedList list;
public Stack() {
list = new LinkedList();
}
public void stackAdd(Object obj) {
list.add(obj);
}
public Object stackRemove() {
return list.removeLast();
}
public int stackLength() {
return list.size();
}
@Override
public String toString() {
return "Stack{" +
"list=" + list +
'}';
}
}
public class ListTest3 {
public static void main(String[] args) {
Stack stack = new Stack(); //调用构造方法,实际上创建的是LinkedList对象
stack.stackAdd("java");
stack.stackAdd("python");
stack.stackAdd("mysql");
stack.stackAdd("hadoop");
System.out.println(stack);
System.out.println("==============");
int l = stack.stackLength();
for (int i = 0; i < l; i++) {
System.out.println(stack.stackRemove());
}
}
}
结果: