一. 集合概述
(一) 集合和数组的区别
1. 相同点: 两者都是数据存储容器,可以存储多个数据
2. 不同点:
数组:
1) 数组的长度是不可变的,集合的长度是可变的
2) 数组可以存基本数据类型和引用数据类型
int[] arr = new int[3]; String[] arr1 = new String[5];
集合只能存引用数据类型,如果要存基本数据类型,需要存对应的包装类
(二) 集合体系结构
List :
1. 元素存取有序
2. 元素有索引
3. 存储的元素可以重复
Set:
1. 元素存取无序
2. 没有索引
3. 不存储重复元素
二. 单列集合体系的详细讲解
(一) 顶层接口Collection常用功能
1. 概述: Collection是单列集合的顶层接口,定义的是所有单列集合中共有的功能. JDK不提供此接口的任何直接实现.它提供更具体的子接口(如Set和List)实现.
2. 创建Collection集合的对象:
Collection接口,不能直接创建对象,因此找一个实现类ArrayList创建对象
1) 多态: 多态的方式 -- 父类引用指向子类对象 Collection c = new ArrayList();
2) 具体的实现类创建 -- 本类引用指向本类对象 ArrayList a = new ArrayList();
3. Collection集合常用方法:
1) boolean add(Object e): 添加元素
2) boolean remove (Object o): 从集合中移除指定的元素
3) void clear(): 清空集合中的元素
4) boolean contains(Object o): 判断集合中是否存在指定的元素
5) boolean isEmpty(): 判断集合是否为空(集合存在,没有元素), 如果集合为空, 那么返回true, 如果集合不为空 false
6) int size(): 返回集合中元素的数量,返回集合的长度。
7) Object[] toArray(): 返回集合的对象的数组
import java.util.ArrayList; import java.util.Collection; public class CollectionMethod { public static void main(String[] args) { Collection c = new ArrayList(); // 一个集合中只存储相同的数据类型 // 1. boolean add(Object e): 添加元素 c.add("abc"); c.add("hello"); c.add("he"); c.add("hi"); System.out.println(c);// [abc, hello, he, hi] // 2. boolean remove (Object o): 从集合中移除指定的元素 c.remove("hello"); System.out.println(c);// [abc, he, hi] c.remove("world"); System.out.println(c);// [abc, he, hi] // 3. boolean contains(Object o): 判断集合中是否存在指定的元素 System.out.println(c.contains("abc"));// true System.out.println(c.contains("world"));// false // 4.boolean isEmpty(): 判断集合是否为空(集合存在,没有元素), // 如果集合为空, 那么返回true, 如果集合不为空 false System.out.println(c.isEmpty());// fasle System.out.println(c.size());// 3 // 5. void clear(): 清空集合中的元素 c.clear(); System.out.println(c);// [] System.out.println(c.isEmpty());// true // 6. int size(): 返回集合中元素的数量,返回集合的长度 System.out.println(c.size());// 0 } }
toArray遍历
import java.util.ArrayList; import java.util.Collection; public class ConnectionToArray遍历 { public static void main(String[] args) { Collection c = new ArrayList(); c.add(15); c.add(1); c.add(-8); System.out.println(c); // 1. 将集合容器c转换成一个数组容器 Object[] objArr = c.toArray(); // 2. 遍历数组相当于间接遍历容器 for(int index = 0; index < objArr.length; index++){ Object obj = objArr[index]; Integer i = (Integer)obj; System.out.println(i); } } }
(二) 单列集合的遍历
2.1迭代器遍历
通过迭代器添加add()会在当前位置后加入值
1. 迭代: 更新换代, 从一个到下一个概念, 迭代相当于遍历的含义
2. 迭代器: 专门对于单列集合进行遍历的对象, 称为迭代器
3. 获取集合迭代器的方法
Iterator<E> iterator(): 返回此集合中元素的迭代器,通过集合对象的iterator()方法得到
方法的返回值结果是Iterator接口, 证明这个方法实际返回的应该是这个接口的实现类对象
4. Iterator中的常用方法:
- boolean hasNext(): 判断当前位置是否有元素可以被取出
- E next(): 获取当前位置的元素,将迭代器对象移向下一个索引位置
- void remove(): 删除迭代器对象当前指向的元素
hashNext和next功能的运行方式:
import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; public class Connection迭代器遍历 { public static void main(String[] args) { Collection c = new ArrayList(); c.add(3.14); c.add(2.1); c.add(-0.3); c.add(99.0); // 1. 获取到c集合的迭代器对象 Iterator it = c.iterator(); // 2. 循环获取出集合c中的每一个元素 // hasNext() : 判断集合中是否还有下一个元素 while(it.hasNext()){ // 获取出集合中的元素 Object obj = it.next(); Double d = (Double)obj; System.out.println(d); } // System.out.println(it.next()); /*System.out.println(it.next()); System.out.println(it.next()); System.out.println(it.next()); System.out.println(it.next()); // NoSuchElementException : 将集合中所有元素都遍历完毕,还想继续向下获取元素,报出次异常 // 没有这个元素异常 System.out.println(it.next());*/ } }
2.2增强for(foreach)遍历
1.概述:
1) 增强for是JDK5之后出现的,其内部原理是一个Iterator迭代器
2) 实现Iterable接口的类才可以使用迭代器和增强for简化数组和Collection集合遍历
2.格式:
for(集合/数组中元素的数据类型 变量名 : 集合/数组名){
// 已经将当前遍历到的元素封装到变量中了,直接使用变量即可
}
import java.util.ArrayList; import java.util.Collection; public class Collection增强for遍历 { public static void main(String[] args) { Collection c = new ArrayList(); c.add(15); c.add(1); c.add(-8); /* for(数据类型 变量名 : 遍历的集合或者数组){ } 数据类型就是集合或者数组中存储的数据类型; 变量名表示每一次循环中, 依次表达集合或数组中的每一个元素 */ for(Object obj : c){ System.out.println((Integer)obj); } int[] arr = {12,13,14,15}; for(int i : arr){ System.out.println(i); } } }
(三) 有序单列集合List
3.1概述
1. 概述: List集合是Collection接口的子接口,其下有两个常用实现类分别为 ArrayList 和 LinkedList
2. List集合特点:
1) 有序:元素存入集合与从集合中取出的顺序一致
2) 有索引:每个元素都有自己的索引编号,从0开始,到集合长度-1为止
3) 元素可以重复:即使是值相同的几个元素,位置和索引也各不相同,可以区分这几个值
3. List集合的特有方法:
1) void add(int index,E element): 在此集合中的指定位置插入指定的元素
2) E remove(int index): 删除指定索引处的元素,返回被删除的元素
3) E set(int index,E element): 修改指定索引处的元素,返回被修改的元素
4) E get(int index):返回指定索引处的元素
4. 针对List集合特有的遍历方式
可以通过集合的size方法获取list集合索引的范围,根据索引通过get方法可以获取指定索引的值。
特有方法代码
import java.util.ArrayList; import java.util.List; public class ListSpecialMethod { public static void main(String[] args) { List list = new ArrayList(); list.add(12); list.add(99); list.add(1); System.out.println(list);// [12, 99, 1] // 1)void add(int index,E element): 在此集合中的指定位置插入指定的元素 list.add(2,22);// [12, 99, 22, 1] System.out.println(list); // 2)E remove(int index): 删除指定索引处的元素,返回被删除的元素 Object obj = list.remove(1); System.out.println(obj);// 99 System.out.println(list);// [12, 22, 1] //3)E set(int index,E element): 修改指定索引处的元素,返回被修改的元素 Object obj1 = list.set(0,888); System.out.println(obj1);// 12 System.out.println(list);// [888, 22, 1] //4)E get(int index):返回指定索引处的元素 Object obj2 = list.get(2); System.out.println(obj2);// 1 } }
List集合特有遍历方式
import java.util.ArrayList; import java.util.List; public class List遍历 { public static void main(String[] args) { List list = new ArrayList(); list.add(12); list.add(99); list.add(1); for(int index = 0; index < list.size(); index++){ Object obj = list.get(index); System.out.println(obj); } } }
3.2 并发修异常的产生原因和解决办法
1. ConcurrentModificationException 并发修改异常
2.异常发生原因: 在迭代器遍历过程中使用集合的引用进行元素的添加或删除
3. 解决方法:
(1) 通过for循环(索引遍历法)遍历集合,在遍历过程中可以进行添加和删除元素
(2) 使用迭代器中的remove()方法删除集合元素
(3) 使用List集合的特有迭代器ListIterator<E>, 通过List中的方法listIterator()获取, 该迭代器允许迭代期间修改列表
-- add(E e) 添加元素
-- remove() 移除元素
案例 : 定义一个List集合, 存储字符串数据”ok”,”java”,”hello”,”world”, 迭代器遍历集合元素, 如果集合中存在”hello”字符串,向集合中添加”end”字符串
import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.ListIterator; public class 并发修改异常 { public static void main(String[] args) { List list = new ArrayList(); list.add("ok"); list.add("java"); list.add("hello"); list.add("world"); // changeList(list); // changeListIterator(list); changeListFor(list); } // 案例 : 定义一个List集合, 存储字符串数据”ok”,”java”,”hello”,”world”, // 迭代器遍历集合元素, 如果集合中存在”hello”字符串,向集合中添加”end”字符串 // changeList方法会发生并发修改问题 public static void changeList(List list){ // 1. 使用迭代器遍历list集合 Iterator it = list.iterator(); while(it.hasNext()){ String str = (String)it.next(); if("hello".equals(str)){ // ConcurrentModificationException // 并发 修改 异常 // 发生原因: 如果对于集合一边使用迭代器遍历,一边修改集合中的数据内容,那么可能并发修改异常 list.add("end"); } } System.out.println(list); } // changeListIterator可以解决并发修改问题 public static void changeListIterator(List list){ ListIterator it = list.listIterator(); while(it.hasNext()){ String str = (String)it.next(); if("hello".equals(str)){ // 解决并发修改异常的关键: 就是迭代的过程中,不允许通过集合对象修改集合元素(新增,删除) // 必须使用迭代器进行集合中元素的修改, 才能解决并发修改异常问题 it.add("end"); } } System.out.println(list); } // List集合特有索引遍历方式, 可以解决并发修改问题 public static void changeListFor(List list){ for(int index = 0; index < list.size(); index++){ String str = (String)list.get(index); if("hello".equals(str)){ // list.add(0,"end"); 死循环 list.add("end"); } } System.out.println(list); } // 增强for循环也会有并发修改问题: 因为底层也是迭代器实现的 public static void changeListForEach(List list){ for(Object obj : list){ String str = (String)obj; if("hello".equals(str)){ // list.add(0,"end"); 死循环 list.add("end"); } } System.out.println(list); } }
(四) 数据结构之栈和队列
数据结构是计算机存储,组织数据的方式,通常情况下, 精心选择的数据结构可以带来更高的运行和存储效率
4.1栈
栈: stack,又称堆栈,它是运算受限的线性表,其限制是仅允许在标的一端进行插入和删除操作,不允许在其他任何位置进行添加、查找、删除等操作。
特点: 先进后出,(即,存进去的元素,要在后它后面的元素依次取出后,才能取出该元素)。例如,子弹压进弹夹,先压进去的子弹在下面,后压进去的子弹在上面,当开枪时,先弹出上面的子弹,然后才能弹出下面的子弹。
压栈:就是存元素。即,把元素存储到栈的顶端位置,栈中已有元素依次向栈底方向移动一个位置。
弹栈:就是取元素。即,把栈的顶端位置元素取出,栈中已有元素依次向栈顶方向移动一个位置。
4.2队列
queue,简称队,它同堆栈一样,也是一种运算受限的线性表,其限制是仅允许在表的一端进行插入,而在表的另一端进行删除。
特点:
1) 先进先出(即,存进去的元素,要在它前面的元素依次取出后,才能取出该元素)。例如,小火车过山洞,车头先进去,车尾后进去;车头先出来,车尾后出来。
2) 队列的入口、出口各占一侧。例如,下图中的左侧为入口,右侧为出口
4.3数据结构之数组和链表
1.数组:
Array:是有序的元素序列,数组是在内存中开辟一段连续的空间,并在此空间存放元素。就像是一排出租屋,有100个房间,从001到100每个房间都有固定编号,通过编号就可以快速找到租房子的人。
特点查找元素快:
通过可以快速访问指定位置的元素
增删元素慢:
指定索引位置增加元素:需要创建一个新数组,将指定新元素存储在指定索引位置,再把原数组元素根据索引,复制到新数组对应索引的位置。如下图
指定索引位置删除元素:需要创建一个新数组,把原数组元素根据索引,复制到新数组对应索引的位置,原数组中指定索引位置元素不复制到新数组中。如下图
2.链表:linked list,由一系列结点node(链表中每一个元素称为结点)组成,结点可以在运行时动态生成。每个结点包括两个部分:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域。我们常说的链表结构有单向链表与双向链表,那么这里给大家介绍的是单向链表。
3.简单的说,采用该结构的集合,对元素的存取有如下的特点:
多个结点之间,通过地址进行连接。例如,多个人手拉手,每个人使用自己的右手拉住下个人的左手,依次类推,这样多个人就连在一起了。
查找元素慢:想查找某个元素,需要通过连接的节点,依次向后查找指定元素
增删元素快: 增加元素 --> 只需要修改连接下个元素的地址即可。
删除元素:只需要修改连接下个元素的地址即可。
4.4 ArrayList和LinkedList对比分析
1. ArrayList集合:
(1) 在创建ArrayList集合对象的时候,会维护一个长度为10的Object类型的数组.
(2) 当插入数据10个长度不够,这时候以1.5倍的形式进行扩容
(3) 存储特点 : 查询快, 增删慢
3.LinkedList:集合数据存储的结构是链表结构。方便元素添加、删除的集合。
LinkedList是一个双向链表,特点 : 查询慢, 增删快, 链表结构不需要连续内存空间, 可以利用内存中零散空间
实际开发中对一个集合元素的添加与删除经常涉及到首尾操作,而LinkedList提供了大量首尾操作的方法。这些方法我们作为了解即可:
public void addFirst(E e):将指定元素插入此列表的开头.
public void addLast(E e):将指定元素添加到此列表的结尾。
public E getFirst():返回此列表的第一个元素。
public E getLast():返回此列表的最后一个元素。
public E removeFirst():移除并返回此列表的第一个元素。
public E removeLast():移除并返回此列表的最后一个元素。
import java.util.LinkedList; public class LinkedListDemo { public static void main(String[] args) { LinkedList list = new LinkedList(); list.addFirst("1"); list.addFirst("2"); System.out.println(list);// [2, 1] list.addLast("end"); list.addLast("ok"); System.out.println(list);// [2, 1, end, ok] list.removeFirst(); System.out.println(list);// [1, end, ok] list.removeLast(); System.out.println(list);// [1, end] String str = (String)list.getFirst(); System.out.println(str);// 1 Object obj = list.getLast();// Object obj = "end"; String s = (String)obj; System.out.println(list.getLast());// end } }
案例:
测试: ArrayList和linkedList首尾增加元素, 首尾删除元素, 首尾获取元素的性能
import java.util.ArrayList; import java.util.LinkedList; public class ArrayListAndLinkedList比较 { public static void main(String[] args) { ArrayList list = new ArrayList(); ArrayListAdd(list); LinkedList list1 = new LinkedList(); LinkedListAdd(list1); ArrayListGet(list); LinkedListGet(list1); ArrayListDelete(list); LinkedListDelete(list1); } // 测试: ArrayList和LinkedList首尾增加元素, 首尾删除元素, 首尾获取元素的性能 // 1. 测试ArrayList首尾新增功能 public static void ArrayListAdd(ArrayList list){// 0x12 long beginTime = System.currentTimeMillis(); for(int i = 1; i <= 10000; i++){ list.add(0,"first"); list.add(list.size()-1,"end"); } long endTime = System.currentTimeMillis(); System.out.println(endTime - beginTime); } // 2. 测试LinkedList首尾新增功能 public static void LinkedListAdd(LinkedList list){ long beginTime = System.currentTimeMillis(); for(int i = 1; i <= 10000; i++){ list.addFirst("first"); list.addLast("end"); } long endTime = System.currentTimeMillis(); System.out.println(endTime - beginTime); } // 3. 测试ArrayList首尾获取功能 public static void ArrayListGet(ArrayList list){ long beginTime = System.currentTimeMillis(); for(int i = 1; i <= 10000; i++){ list.get(0); list.get(list.size()-1); } long endTime = System.currentTimeMillis(); System.out.println(endTime - beginTime); } // 4. 测试ArrayList首尾获取功能 public static void LinkedListGet(LinkedList list){ long beginTime = System.currentTimeMillis(); for(int i = 1; i <= 10000; i++){ list.getFirst(); list.getLast(); } long endTime = System.currentTimeMillis(); System.out.println(endTime - beginTime); // 5. 测试ArrayList首尾删除功能 public static void ArrayListDelete(ArrayList list){ long beginTime = System.currentTimeMillis(); for(int i = 1; i <= 10000; i++){ list.remove(0); list.remove(list.size()-1); } long endTime = System.currentTimeMillis(); System.out.println(endTime - beginTime); } // 6. 测试ArrayList首尾删除功能 public static void LinkedListDelete(LinkedList list){ long beginTime = System.currentTimeMillis(); for(int i = 1; i <= 10000; i++){ list.removeFirst(); list.removeLast(); } long endTime = System.currentTimeMillis(); System.out.println(endTime - beginTime); } }