目录
一.Collection
二.List集合
三.ArrayList集合
四.LinkedList集合
五.Set集合
六.hashSet集合
七.LinkedHashSet集合
八.TreeSet集合
九.集合工具类Collections
集合体系概述
单列集合:Collection代表单列集合,每个元素(数据)只包含一个值。
双列集合:Map代表双列集合,每个元素包含两个值(键值对)。
单列集合:
List系列集合:添加的元素是有序、可重复、有索引
ArrayList:有序、可重复、有索引
LinekdList:有序、可重复、有索引
Set系列集合:添加的元素是无序、不重复、无索引
HashSet:无序、不重复、无索引
LinkedHashSet:有序、不重复、无索引
TreeSet:按照大小默认升序排序、不重复、无索引
一.Collection
为什么要先学Collection的常用方法?
Collection是单列集合的祖宗,它规定的方法(功能)是全部单列集合都会继承的。
Collection的常用方法:
代码展示:
package com.itheima.day06.teacher.g_collection;import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;/*** Collection常用方法*/
public class CollectionDemo {public static void main(String[] args) {/*Collection 是 ArrayList祖宗*/
// Collection<String> con = new ArrayList<>();//多态
// ArrayList<String> list = new ArrayList<>();//本态//采用多态?为什么呢? 多态特点 多态下只能调用 父中定义的功能,不会调用子类特有的。// 我们要研究是 共性的功能 使用多态Collection<String> con = new ArrayList<>();//多态// add()con.add("name1");con.add("name2");con.add("name3");con.add("name4");System.out.println(con);//size() 获取集合长度 元素个数System.out.println("集合中有"+con.size()+"个元素");// boolean contains(元素) 判断集合中是否包含指定元素System.out.println("是否包含张三 "+con.contains("张三"));//falseSystem.out.println("是否包含赵四 "+con.contains("赵四"));//true//boolean remove(元素) 删除指定元素 返回 是否删除成功System.out.println("删除一下:王老七 "+con.remove("王老七"));System.out.println(con);//情况集合方法 void clear()con.clear();System.out.println("清空集合之后:"+con);// isEmpty() 判断集合是否为空System.out.println("con当前是空的吗?"+con.isEmpty());// 重写添加数据con.add("谢大脚");con.add("王小蒙");con.add("香秀");con.add("王云");// Object[] toArray() 变成数组Object[] array = con.toArray();System.out.println(Arrays.toString(array));//扩展一个Collection<String> c1 = new ArrayList<>();//多态// add()c1.add("name1");c1.add("name2");c1.add("name3");c1.add("name4");Collection<String> c2 = new ArrayList<>();//多态// add()c2.add("name1");c2.add("name2");c2.add("name3");c2.add("name4");c1.addAll(c2);//c2集合内容 批量添加到c1中System.out.println(c1);}
}
Collection的遍历方式:
1.迭代器:
迭代器是用来遍历集合的专用方式(数组没有迭代器),在Java中迭代器的代表是Iterator。
Collection集合获取迭代器的方法 Iterator<E> iterator() 返回集合中的迭代器对象,该迭代器对象默认指向当前集合的第一个元素
Iterator迭代器中的常用方法:
boolean hasNext():询问当前位置是否有元素存在,存在返回true ,不存在返回false
E next():获取当前位置的元素,并同时将迭代器对象指向下一个元素处。
2.增强for循环:
格式:for (元素的数据类型 变量名 : 数组或者集合) { }
增强for可以用来遍历集合或者数组。
增强for遍历集合,本质就是迭代器遍历集合的简化写法。
3.lambda表达式
得益于JDK 8开始的新技术Lambda表达式,出现了一种更简单、更直接遍历集合的方式-Lambda表达式。
需要使用Collection的如下方法来完成:default void forEach(Consumer<? super T> action)
lists.forEach(s -> {System.out.println(s); });
package com.itheima.day06.teacher.g_collection;import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;public class CollectionDemo2 {public static void main(String[] args) {/*Collection 是 ArrayList祖宗*/
// Collection<String> con = new ArrayList<>();//多态
// ArrayList<String> list = new ArrayList<>();//本态//采用多态?为什么呢? 多态特点 多态下只能调用 父中定义的功能,不会调用子类特有的。// 我们要研究是 共性的功能 使用多态Collection<String> con = new ArrayList<>();//多态// add()con.add("name1");con.add("name2");con.add("name3");con.add("name4");System.out.println(con);//size() 获取集合长度 元素个数
// for (int i = 0; i < con.size(); i++) {
// String s = con.get(i);//Conllection 认为无索引
// }// Collection集合的遍历 需要借助一个工具 来完成遍历(迭代)// 迭代器 Interator// 每一个集合对象 都有自己的 迭代器。// 呼叫迭代器 获取迭代器Iterator<String> it = con.iterator();// iterator 可以帮助集合完成 元素的获取// 迭代器指向第一个元素while(it.hasNext()){//询问 当前位置是否有元素 有true false//再去获取String e = it.next();System.out.println(e);}}
}-------------package com.itheima.day06.teacher.g_collection;import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;public class CollectionDemo3 {public static void main(String[] args) {/*Collection 是 ArrayList祖宗*/
// Collection<String> con = new ArrayList<>();//多态
// ArrayList<String> list = new ArrayList<>();//本态//采用多态?为什么呢? 多态特点 多态下只能调用 父中定义的功能,不会调用子类特有的。// 我们要研究是 共性的功能 使用多态Collection<String> con = new ArrayList<>();//多态// add()con.add("name1");con.add("name2");con.add("name3");con.add("name4");System.out.println(con);//因为开发中 大量场景都要使用到 集合 都要去完成遍历 所以jdk 升级的时候 做了一个很高明的操作 就是简化迭代器的使用/*增强for - --单列集合 数组for(数据类型 变量名 :被遍历的集合/数组){}*/
// for(String s:con){
// System.out.println(s);
// }//集合.forfor (String s : con) {System.out.println(s);}//增强for 只适合于获取数据 不适合与修改}
}----------package com.itheima.day06.teacher.g_collection;import java.util.ArrayList;
import java.util.Collection;
import java.util.function.Consumer;public class CollectionDemo4 {public static void main(String[] args) {/*Collection 是 ArrayList祖宗*/
// Collection<String> con = new ArrayList<>();//多态
// ArrayList<String> list = new ArrayList<>();//本态//采用多态?为什么呢? 多态特点 多态下只能调用 父中定义的功能,不会调用子类特有的。// 我们要研究是 共性的功能 使用多态Collection<String> con = new ArrayList<>();//多态// add()con.add("name1");con.add("name2");con.add("name3");con.add("name4");System.out.println(con);/*jdk8提供的 foreach*/con.forEach(new Consumer<String>() {@Overridepublic void accept(String s) {//s 接收每次得到的数据System.out.println(s);}});System.out.println("=================");con.forEach(s -> System.out.println(s));con.forEach(System.out::println);}
}
二.List集合
1.List集合因为支持索引,所以多了很多与索引相关的方法
特有方法:
2.遍历方式:
1.for循环(因为List集合有索引)
2.迭代器
3.增强for循环
4.Lambda表达式
package com.itheima.day07.teacher.list01;import java.util.ArrayList;
import java.util.List;public class ListDemo {/*Collection 单列集合顶层接口里面定义关于元素的 一些 获取添加删除的通用方法--- List 子接口 有序集合特点:有序 可以重复 有索引里面 有写特有和索引相关方法增 void add(int index,E e) 往集合指定位置添加元素删 E remove(int index) 根据指定索引进行删除 返回被删除元素改 E set(int index,E e) 根据索引进行元素替换,返回被替换的元素查 E get(int index) 根据索引获取元素*/public static void main(String[] args) {// 这是多态写法 因为在多态下只能使用List 接口定义的方法List<String> list = new ArrayList<>();//添加元素list.add("name1");list.add("name2");list.add("name3");list.add("name4");System.out.println(list);//有序--存取顺序一致。且有索引// 索引为3的位置添加一个 真玲list.add(3,"真玲");System.out.println(list);// 删除// 删除索引为4的元素String remove = list.remove(4);System.out.println("删除索引为4的元素:"+remove);System.out.println("删除后的集合:"+list);
// System.out.println(list.remove(4)); 报错 索引越界//修改 索引为2的 改为狗蛋System.out.println(list.set(2,"狗蛋"));System.out.println("修改后的集合:"+list);// 获取 索引为1的元素System.out.println(list.get(1));}
}-----------
package com.itheima.day07.teacher.list01;import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;public class ListDemo02 {public static void main(String[] args) {/*完成集合的遍历遍历方式 四种*/List<String> list = new ArrayList<>();list.add("name1");list.add("name2");list.add("name3");list.add("name4");//普通for循环 list集合.forifor (int i = 0; i < list.size(); i++) {System.out.println(list.get(i));}System.out.println("===============");//迭代器 iterator()Iterator<String> it = list.iterator();while(it.hasNext()){System.out.println(it.next());}System.out.println("===============");//增强for list集合.forfor (String s : list) {System.out.println(s);}// lambda 集合.forEachlist.forEach(s -> System.out.println(s));System.out.println("===============");list.forEach(System.out::println);}
}
三.ArrayList集合
1.ArrayList集合底层是基于数组实现的
2.数组的特点:查询快,增删慢(数组长度固定,增删需要新数组效率低)
3.ArrayList集合对象的创建和扩容过程
1、利用无参构造器创建的集合,会在底层创建一个默认长度为0的数组
2、添加第一个元素时,底层会创建一个新的长度为10的数组
3、存满时,会扩容1.5倍
4、如果一次添加多个元素,1.5倍还放不下,则新创建数组的长度以实际为准
4.ArrayList集合适合什么业务场景?不适合什么业务场景?
适合根据索引查询数据的场景或者数据量不是很大的场景
不适合数据量很大时又要频繁地进行增删操作的场景
因为比较常用我单独写了一篇
四.LinkedList集合
1.LinkedList集合底层是基于双链表实现的
2.链表中的结点是独立的对象,在内存中是不连续的,每个结点包含数据值和下一个结点的地址。
3.链表的特点:查询慢,都要从头开始找,增删快
4.双链表的特点:
每个节点包含数据值和上一个节点的地址以及下一个节点的地址
查询慢,增删相对较快,但对首尾元素进行增删改查的速度是极快的
5.LinkedList新增了:很多首尾操作的特有方法。
6.LinkedList集合适合需要频繁操作首尾元素的场景,比如栈和队列
package com.itheima.day07.teacher.list02;import java.util.LinkedList;public class LikendListDemo01 {public static void main(String[] args) {/*用LinkedList模拟 小火车过山洞...模拟 队列结构队列结构先进先出 FIFO*/LinkedList<String> list = new LinkedList<>();//入队 进山洞list.addLast("火车头");list.addLast("第一节车厢");list.addLast("第二节车厢");list.addLast("第三节车厢");list.addLast("火车尾");//出山洞System.out.println(list.removeFirst());System.out.println(list.removeFirst());System.out.println(list.removeFirst());System.out.println(list.removeFirst());System.out.println(list.removeFirst());}
}------------package com.itheima.day07.teacher.list02;import java.util.LinkedList;public class LinkedListDemo02 {public static void main(String[] args) {/*模拟弹夹 发射子弹栈结构特点 先进后出 FILO*/LinkedList<String> danJia = new LinkedList<>();// 压 子弹danJia.addFirst("第一颗子弹");//每次都在最上面danJia.addFirst("第二颗子弹");//每次都在最上面danJia.addFirst("第三颗子弹");//每次都在最上面danJia.addFirst("第四颗子弹");//每次都在最上面// 射子弹System.out.println(danJia.removeFirst());//每次射最上面的System.out.println(danJia.removeFirst());System.out.println(danJia.removeFirst());System.out.println(danJia.removeFirst());}
}
五.Set集合
整体特点: 无序 ,存取顺序不保证一致; 不重复; 无索引;
HashSet: 无序、不重复、无索引
LinkedHashSet:有序、不重复、无索引
TreeSet:可排序、不重复、无索引
六.hashSet集合
1.哈希值:
1.就是一个int类型的数值,Java中每个对象都有一个哈希值
2.Java中的所有对象,都可以调用Obejct类提供的hashCode方法,返回该对象自己的哈希值
2.对象哈希值的特点:
同一个对象多次调用hashCode()方法返回的哈希值是相同的。
不同的对象,它们的哈希值一般不相同,但也有可能会相同(哈希碰撞)。
3.HashSet底层基于哈希表实现
JDK8之前,哈希表 = 数组+链表:
1.创建一个默认长度16的数组,默认加载因子为0.75,数组名table
2.使用元素的哈希值对数组的长度求余计算出应存入的位置
3.判断当前位置是否为null,如果是null直接存入
4.如果不为null,表示有元素,则调用equals方法比较
JDK8开始,哈希表 = 数组+链表+红黑树(一种平衡的二叉树)
JDK8开始,当链表长度超过8,且数组长度>=64时,自动将链表转成红黑树
JDK8开始后,哈希表中引入了红黑树后,进一步提高了操作数据的性能。
哈希表是一种增删改查数据性能都较好的结构。
4.数据结构:树
二叉查找树:规则:小的往左存,大的往右存,一样的不存
可能存在的问题:当数据已经是排好序的,导致查询的性能与单链表一样,查询速度变慢!
平衡二叉树:在满足二叉查找树的大小规则下,让树尽可能矮小,以此提高查数据的性能。
红黑树:是一种可以自平衡的二叉树,红黑树是一种增删改查数据性能相对都较好的结构
5.如何让HashSet集合能够实现对内容一样的两个不同对象也能去重复?
重写hashCode()和equals()方法
package com.itheima.day07.teacher.set02;import java.util.Objects;/*** 学术类*/
public class Student {private String name;private int age;private double height;public Student() {}public Student(String name, int age, double height) {this.name = name;this.age = age;this.height = height;}/*** 获取* @return name*/public String getName() {return name;}/*** 设置* @param name*/public void setName(String name) {this.name = name;}/*** 获取* @return age*/public int getAge() {return age;}/*** 设置* @param age*/public void setAge(int age) {this.age = age;}/*** 获取* @return height*/public double getHeight() {return height;}/*** 设置* @param height*/public void setHeight(double height) {this.height = height;}public String toString() {return "Student{name = " + name + ", age = " + age + ", height = " + height + "}";}//利用idea快捷键 hashCode equals/*比较两个对象的内容*/@Overridepublic boolean equals(Object o) {if (this == o) return true;if (o == null || getClass() != o.getClass()) return false;Student student = (Student) o;return age == student.age && Double.compare(student.height, height) == 0 && Objects.equals(name, student.name);}/*根据属性算哈希值 尽量不一样的属性 值不同 减少哈希碰撞*/@Overridepublic int hashCode() {return Objects.hash(name, age, height);}
}------------
package com.itheima.day07.teacher.set02;import java.util.HashSet;public class HashSetDemo {public static void main(String[] args) {/*创建一个学生集合 用来存储不同 学生对象*/HashSet<Student> set = new HashSet<>();//创建学生对象Student stu1 = new Student("小磊",18,1.88);Student stu2 = new Student("小鑫",28,1.38);Student stu3 = new Student("小哲",22,1.78);Student stu4 = new Student("小鑫",28,1.38);set.add(stu1);set.add(stu2);set.add(stu3);set.add(stu2);set.add(stu4);//我们没有重写 hashCode equals的时候可以存储进去的,// 原因是 存的时候先调用 hashCode方法(Object--根地址有一定关系) 哈希code不一样.// 即使哈希一样,也能存!!没有重写equals 比较的是地址值 不是一个元素。/*在开发过程中是地址值不一样就是不同对象吗?其实不尽然,我们开发中认为 两个对象如果 所有属性值都一样 认为是 同一个对象。在我们观点里面 stu2 stu4是同一个对象。默认情况下没有当成 同一个对象 并没有完成 元素的去重如果 想要达到开发标准 两个对象属性值都一样 就是同一个对象怎么做?重写hashCode() -- 目的 是要根据内容算哈希值。equals() -- 目的 是进行内容比较。重写之后stu2 stu4 先算出哈希值 哈希碰撞 位置一样 再进行equals比较 内容一样不存*/for (Student student : set) {System.out.println(student);}/*存储元素原理先根据hashcode算出 存储位置位置上有元素 就进行equals比较。*/}
}
七.LinkedHashSet集合
依然是基于哈希表(数组、链表、红黑树)实现的
但是,它的每个元素都额外的多了一个双链表的机制记录它前后元素的位置
八.TreeSet集合
1. 特点:不重复、无索引、可排序(默认升序排序 ,按照元素的大小,由小到大排序)
对于数值类型:Integer , Double,默认按照数值本身的大小进行升序排序。
对于字符串类型:默认按照首字符的编号升序排序。
2.底层是基于红黑树实现的排序。
3.TreeSet集合中对自定义类型元素排序方案:
方式一:让自定义的类(如学生类)实现Comparable接口,重写里面的compareTo方法来指定比较规则。
方式二:通过调用TreeSet集合有参数构造器,可以设置Comparator对象(比较器对象,用于指定比较规则)
package com.itheima.day07.teacher.set03;/**学生类
*/public class Student implements Comparable<Student> {private String name;private int age;private double height;public Student() {}public Student(String name, int age, double height) {this.name = name;this.age = age;this.height = height;}/*** 获取* @return name*/public String getName() {return name;}/*** 设置* @param name*/public void setName(String name) {this.name = name;}/*** 获取* @return age*/public int getAge() {return age;}/*** 设置* @param age*/public void setAge(int age) {this.age = age;}/*** 获取* @return height*/public double getHeight() {return height;}/*** 设置* @param height*/public void setHeight(double height) {this.height = height;}public String toString() {return "Student{name = " + name + ", age = " + age + ", height = " + height + "}";}@Overridepublic int compareTo(Student o) {return this.age-o.age;//前-后 升序}
}-------------
package com.itheima.day07.teacher.set03;import java.util.Comparator;
import java.util.TreeSet;public class TreeSetDemo {/*TreeSet底层是一个 二叉排序树遵循特点小的存左边 大的存右边 一样的不存里面的存储的元素必须具备 !!排序功能!! 元素之间有大小关系*/public static void main(String[] args) {TreeSet<Integer> set1 = new TreeSet<>();set1.add(13);set1.add(15);set1.add(12);set1.add(33);set1.add(26);set1.add(13);set1.add(14);System.out.println(set1);/*TreeSet去重原则 一样的不存存完之后元素进行排序是因为 存储的元素类型有排序规则implements Comparable<Integer>实现 Comparable 代表有了排序的规则 所以可以使用TreeSet存储TreeSet 去重的原理是排序的规则 如果一样就不存。*/System.out.println("==========");TreeSet<String> set2 = new TreeSet<>();set2.add("cba");set2.add("cbb");set2.add("aba");set2.add("bbb");set2.add("cbb");System.out.println(set2);}
}-------------
package com.itheima.day07.teacher.set03;import java.util.Comparator;
import java.util.TreeSet;public class TreeSetDemo01 {/*开发中不建议 用TreeSet存储自定义类型,因为会根据只定义排序内容进行去重,与实际开发不符。如果执意要用 你就得对所有的 属性进行排序比较 规则如果要用TreeSet存自定义类型1:自定义类型 实现 Comparable接口 实现排序规则。2:在 TreeSet 构造中 写出临时的排序规则 传递规则Comparator接口。*/public static void main(String[] args) {//创建学生对象Student stu1 = new Student("小磊",18,1.88);Student stu2 = new Student("小鑫",28,1.38);Student stu3 = new Student("小哲",22,1.78);Student stu4 = new Student("小迪",28,1.68);//创建 TreeSet集合TreeSet<Student> set = new TreeSet<>(new Comparator<Student>() {@Overridepublic int compare(Student o1, Student o2) {return o2.getAge()-o1.getAge();//按照年龄降序}});//存元素set.add(stu1);set.add(stu2);set.add(stu3);set.add(stu4);for (Student student : set) {System.out.println(student);}}
}
Collection集合使用小结:
1、如果希望记住元素的添加顺序,需要存储重复的元素,又要频繁的根据索引查询数据?
用ArrayList集合(有序、可重复、有索引),底层基于数组的。(常用)
2、如果希望记住元素的添加顺序,且增删首尾数据的情况较多?
用LinkedList集合(有序、可重复、有索引),底层基于双链表实现的。
3.如果不在意元素顺序,也没有重复元素需要存储,只希望增删改查都快?
用HashSet集合(无序,不重复,无索引),底层基于哈希表实现的。(常用)
4.如果希望记住元素的添加顺序,也没有重复元素需要存储,且希望增删改查都快?
用LinkedHashSet集合(有序,不重复,无索引),底层基于哈希表和双链表,
5.如果要对元素进行排序,也没有重复元素需要存储?且希望增删改查都快?
用Treeset集合,基于红黑树实现。
集合的并发修改异常:
使用迭代器遍历集合时,又同时在删除集合中的数据,程序就会出现并发修改异常的错误"王麻子”);("小李子");由于增强for循环遍历集合就是迭代器遍历集合的简化写法,因此,使用增强for循环遍历("李爱花");在同时("张全蛋");("晓李");删除集合中的数据时,程序也会出现并发修改异常的错误。("李玉刚");
怎么保证遍历集合同时删除数据时不出bug?
使用迭代器遍历集合,但用迭代器自己的删除方法删除数据即可
如果能用for循环遍历时:可以倒着遍历并删除;或者从前往后遍历,但删除元素后做i--操作
九.集合工具类Collections
Collections代表集合工具类,提供的都是静态方法,用来操作集合元素的。
常用静态方法:
public static <T> void sort(List<T> list):
数值型的List集合,按照值特性排序,默认升序
字符串的List集合,按照字符串元素的首字母编号排序。默认升序
对于自定义类型的集合,List<Student> ,默认报错的解决方法
解决方式一:让学生类实现比较规则Comparable接口。重写比较方法。
解决方式二:可以让sort方法自带一个比较器
package com.itheima.day07.teacher.other;import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;public class EditException {/*并发修改异常什么是并发修改异常:ConcurrentModificationException在使用迭代器(增强for) 遍历元素的同时,通过集合对象删除了集合中的指定数据,这个时候就会出现并发修改异常。原因是什么:迭代器里面 元素个数 和 集合中元素个数不一致了。怎么避免并发修改异常:在开发中不在迭代器 迭代过程中 使用集合删除对象。使用迭代器去删 迭代器删除的同时会给集合进行同步。*/public static void main(String[] args) {List<String> list = new ArrayList<>();list.add("name1");list.add("name2");list.add("name3");list.add("name3");list.add("name4");list.add("name5");System.out.println(list); // [王麻子, 小李子, 李爱花, 张全蛋, 晓李, 李玉刚]//需求:找出集合中带"李"字的姓名,并从集合中删除Iterator<String> it = list.iterator();while(it.hasNext()){String name = it.next();if(name.contains("李")){
// list.remove(name);不用集合删 用迭代器删除it.remove();//迭代器删除 会同步给集合}}System.out.println(list);}
}-------------
package com.itheima.day07.teacher.other;import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;public class CollenctionsDemo {/*集合工具类使用*/public static void main(String[] args) {//ArrayList集合 是Collection子类 也是List子类List<String> list = new ArrayList<>();//
// list.add("a"); 一个一个元素添加//批量添加 Collections.addAll(Collection集合,T...t)//T 泛型的意思 集合里面是什么类型 泛型就是什么类型Collections.addAll(list,"nba","cba","qba","abc");System.out.println("查看集合内容:"+list);// 打乱顺序方法 --针对有序集合的(List)// Collections.shuffle(List集合)Collections.shuffle(list);System.out.println("打乱后集合内容:"+list);// 对集合元素排序 --针对有序集合(List)// Collections.sort(List集合) 有前提 元素类型 具备排序规则 需要实现Comparable接口Collections.sort(list); //按照默认顺序 升序System.out.println("排序之后:"+list);// 临时改变排序规则// Collections.sort(List集合,Comparator接口实现类对象) 按照当前自己定义的排序规则排序Collections.sort(list, new Comparator<String>() {@Overridepublic int compare(String o1, String o2) {return o2.compareTo(o1);//字符串进行 降序 后面比较前面}});System.out.println("自定义排序规则后:"+list);}
}