一. ArrayList嵌套
-
定义
- 在集合中存放集合,和二维数组类似
-
演示
public static void main(String[] args) { //集合中的元素还是集合 ArrayList<ArrayList<Student>> school = new ArrayList<>(); ArrayList<Student> clas1 = new ArrayList<>(); clas1.add(new Student("小红",18)); clas1.add(new Student("小明", 20)); school.add(clas1); ArrayList<Student> clas2 = new ArrayList<>(); clas2.add(new Student("小张",18)); clas2.add(new Student("小李", 20)); school.add(clas2); ArrayList<Student> clas3 = new ArrayList<>(); clas3.add(new Student("tom",18)); clas3.add(new Student("jack", 20)); school.add(clas3); System.out.println(school); }
-
测试题
- 需求: 先有一家宾馆,宾馆中有多个房间, 每个房间有数量不等客人, 使用集合嵌套的形式模拟并打印所有人的信息
二. Set集合
-
定义
- Set集合存数的元素是无序的, 而且不允许储存重复的元素, 每当有新的元素存入的时候,Set集合会先去过滤, 如果发现和集合中现有元素出现重复, 就不在允许添加
-
应用场景
- 当我们不想让集合中出现重复的元素时,使用Set集合
- 当我们需要排除重复数据时,使用Set集合
-
演示
public static void main(String[] args) { //集合中的元素还是集合 HashSet<Student> s = new HashSet<>(); Student student = new Student("小红", 18); Student student2 = new Student("小红", 18); s.add(student); s.add(student2); Iterator<Student> it = s.iterator(); while (it.hasNext()) { Student student3 = it.next(); System.out.println(student3); } }
三. HashSet
-
定义
- HashSet集合中的元素是通过hash值来比较是否相同
- 集合通过元素的hashCode和equals方法来比较两个元素是否相同, 不同就存入, 相同不存入
- 元素存入的位置未知,和存入的顺序无关
-
存储原理
- HashSet最后还是存入数组中, 只是根据元素的Hash值来确定存入的角标位置
- 元素的hash值 ^ (元素的hash值 >>> 16) & (数组的长度-1)
- HashSet中不是直接存入我们给定的元素, 而是用集合中的一个内部类封装我们存入的元素,所以当我们存入null的时候,不会因为和数组中角标位上默认值null起冲突
- 如果两个不同的元素根据不同的Hash值计算出了同一个角标值,那么都能存进去
- HashSet最后还是存入数组中, 只是根据元素的Hash值来确定存入的角标位置
-
构造方法
- HashSet() 构造出一个新的集合, 底层数组默认的初始容量是16(扩容一倍),加载因子是0.75
- 加载因子: 集合中的数组可用的
- 角标值得可选范围越小,计算出重复角标值得概率就越高
- HashSet(Collection<? extends E> c) 构造一个包含指定collection中元素的新set
- HashSet() 构造出一个新的集合, 底层数组默认的初始容量是16(扩容一倍),加载因子是0.75
-
常用方法
- boolean add( E e) 如果此set集合中尚未包含指定元素,则添加指定元素
- boolean remove(Object o) 移除某个元素 []
- int size() 获取集合的长度
-
演示
- 需求: 去除ArrayList集合中的重复元素
public static void main(String[] args) { ArrayList<String> al = new ArrayList<>(); al.add("a"); al.add("s"); al.add("x"); al.add("a"); al.add("s"); HashSet<String> hs = new HashSet<>(al); System.out.println(hs); }
-
测试题
- 需求: 创建Student类, 定义name和age属性, 创建多个对象(有属性重复的对象)存入ArrayList集合中, 然后对集合中的元素去重
四. HashSet集合中元素保证唯一性
-
原理解析
- HashSet是通过调用元素的hashCode和equals方法来比较两个元素是否形同的,所以如果要保证引用数据类型逻辑上的唯一性,就必须重写hashCode方法和equals方法
- 演示
public int hashCode() { final int prime = 31; int result = 1; result = prime * result + age; result = prime * result + ((name == null) ? 0 : name.hashCode()); return result; }
- prime = 31 的原因
- 别人选的, 没有原因
- 是质数, 和别的值计算得出的数重复的概率低
- 大小适中, 不会出现太大导致结果无法使用的问题
- 便于计算 2<<5 -1
-
引用数据类型除重
- 重写hashCode和equals方法,自定义比较内容
-
测试题
- 需求: 编写程序, 获取10个1到20的随机数, 要求随机数不能重复, 打印结果
public static void main(String[] args) { Random random = new Random(); HashSet<Integer> hashSet = new HashSet<>(); while(hashSet.size()<10){ int num = random.nextInt(20)+1; hashSet.add(num); } System.out.println(hashSet); }
-
测试题
- 需求: 从键盘录入一行数据, 去掉其中重复的字符,打印结果
public static void main(String[] args) { Scanner sc = new Scanner(System.in); System.out.println("请输入一行字符串:"); String line = sc.nextLine(); //将键盘录入的字符串存储在line中 char[] arr = line.toCharArray(); //将字符串转换成字符数组 HashSet<Character> hs = new HashSet<>(); //创建HashSet集合对象 for (int i = 0; i < arr.length; i++) { hs.add(arr[i]); } Iterator<Character> it = hs.iterator(); while (it.hasNext()) { System.out.print(it.next()); } sc.close(); }
-
测试题
- 产生10个随机的字符串, 要求不能重复(字符串中的字符取值在 ‘a’ 到 ‘z’ , ‘A’ 到 ‘Z’ ,‘0’ 到 ‘9’),字符串的长度1-20
五. LinkedHashSet
-
定义
- 兼顾了linked的有序性和HashSet的元素唯一性
-
演示
public static void main(String[] args) { HashSet<String> hashSet = new HashSet<>(); hashSet.add("b"); hashSet.add("c"); hashSet.add("a"); hashSet.add("d"); System.out.println(hashSet); //结果 : [a, b, c, d] LinkedHashSet<String> lhs = new LinkedHashSet<>(); lhs.add("b"); lhs.add("c"); lhs.add("a"); lhs.add("d"); System.out.println(lhs); //结果: [b, c, a, d] }
六. TreeSet集合
-
定义
- TreeSet是一种顺序的集合, 记住, 这里的顺序是指集合中的元素有顺序, 她是通过比较元素的大小来存放的, 大的存右边,小的存左边
- 存入的元素必须实现Comparable接口,并且重写comparTo方法
- 最后存入的元素会形成一个树状结构
-
构造方法
- TreeSet() 构造一个新的空set, 该set根据其元素的自然顺序进行排序
- TreeSet(Comparator <? super E> comparator) 构建一个空的TreeSet, 他根据指定比较器进行排序
-
常用方法
- add(E e) 将指定元素添加到此set
- first() 返回此set中当前第一元素
- last() 返回此set中当前最后一个元素
- floor() 返回此set中小于等于给定元素的最大元素,不存在则返回null
- higher() 返回此set中严格大于给定元素的最小元素,不存在则返回null
-
演示
public static void main(String[] args) { TreeSet<String> set = new TreeSet<>(); set.add("a"); set.add("d"); set.add("c"); set.add("b"); set.add("e"); set.add("b"); set.add("n"); System.out.println(set);//结果: [a, b, c, d, e, n] }
-
添加原理
- TreeSet会将第一个添加的元素作为中点, 以后添加的元素会先跟中点进行比较, 如果大于就接着跟所比较元素的右边的元素接着比较,如果小于就接着跟所比较元素左边的元素接着比较, 直到无法找到可比较的元素,就会将新添加的这个元素放到当前位置
-
Comparable接口
- 所有存入的元素在比较的时候,如果集合没有比较器, 就会将元素本身转成Comparator类型并调用comparTo方法进行比较, 所以我们必须实现Comparato接口,并重写comparTo方法
- 调用compartTo方法比较
- 如果方法返回一个小于0的数,表示当前元素小于被比较元素
- 如果返回 0 表示当前元素等于被比较元素
- 如果返回一个大于0的数, 表示当前元素大于被比较元素
- 演示
public class Student implements Comparable<Student>{ @Override public int compareTo(Student o) { return 0; } }
-
Comparator比较器
- 这是一个接口, 定义了一个compare抽象方法
- compare方法可以对两个元素进行比较
- 如果方法返回一个小于0的数,表示参数1小于参数2
- 如果返回 0 ,表示参数1等于参数2
- 如果返回一个大于0的数, 表示参数1大于参数2
- 演示
public class MyComparator implements Comparator<Student>{ @Override public int compare(Student o1, Student o2) { return 1; } }
-
注意事项
- TreeSet集合的存储原理限定了必须要对存入的元素进行比较
- 要么存入的元素自身实现Comparable接口, 要么提供外部的实现了Comparator接口的比较器, 否则程序运行就会报错
-
测试题
- 需求: 键盘录入5个学生信息(姓名,语文成绩,数学成绩,英语成绩),按照总分从高到低输出到控制台
- 演示
public class MyComparator implements Comparator<Student>{ @Override public int compare(Student o1, Student o2) { int num = o2.getSum() - o1.getSum(); //根据学生的总成绩降序排列 return num ; } }
public static void main(String[] args) { TreeSet<Student> set = new TreeSet<>(new MyComparator()); Student student1 = new Student("小红",100); Student student2 = new Student("小明",50); Student student3 = new Student("小李",60); set.add(student1); set.add(student2); set.add(student3); for (Student student : set) { System.out.println(student); } }
七. 超级for
-
定义
- Iterator的简写形式
- 简化数组和Collection集合的遍历
-
格式
for(元素数据类型 变量 : 数组或者Collection集合) { 使用变量即可,该变量就是当前元素 }
-
演示
public static void main(String[] args) { TreeSet<Student> set = new TreeSet<>(new MyComparator()); Student student1 = new Student("小红"); Student student2 = new Student("小明"); Student student3 = new Student("小李"); set.add(student1); set.add(student2); set.add(student3); for (Student student : set) { System.out.println(student); } }
-
三种迭代方式的删除
- 普通for循环,可以删除,但是需要角标
- 迭代器,可以删除,但是必须使用迭代器自身的remove方法,否则会出现并发修改异常
- 超级for循环不能删除
八. Collections工具类的使用
-
定义
- 一个用于操作Collection集合工具类
-
常用方法
- sort(List list) 根据元素的自然顺序排序
- swap(List list , int i , int j) 交换集合中两个角标位上的值
- reverse(List<?> list ) 反转集合中的元素的顺序
- replaceAll(List list, T oldVal, T newVal) 替换
-
演示
public static void main(String[] args) { List<String> cl = new ArrayList<>(); cl.add("a"); cl.add("d"); cl.add("s"); cl.add("t"); cl.add("a"); cl.add("e"); System.out.println(cl);//[a, d, s, t, a, e] Collections.sort(cl); System.out.println(cl);//[a, a, d, e, s, t] Collections.swap(cl,1,2); System.out.println(cl);//[a, d, a, e, s, t] Collections.reverse(cl); System.out.println(cl);//[t, s, e, a, d, a] Collections.replaceAll(cl,"a","f"); System.out.println(cl);//[t, s, e, f, d, f] }
总结
-
ArrayList集合的嵌套
类似于二维数组, 集合中存储集合
-
Set
无序 : 集合无序/容器无序
去重(有用) : 如果代码中没有去重,不要使用set集合, 如果有去重, 一定要set集合,效率贼高
-
HashSet
底层是数组, 元素需要根据hash值计算出一个角标,在存到数组中 , 所以, 集合中元素的数量不影响去重的效率
自定一定类型, 必须要重写hashCode和equals方法才能去
-
LinkedHashSet
容器有序的还能去重的集合
两边的效率都不高
-
TreeSet
去重, 元素还是有序的
红黑树
集合必须有比较的功能: 1. 元素本身具备比较的能力, 2. 使用第三方比较器
-
超级for
迭代器的简写形式 , 省略很多重复的步骤
不能删除
-
Collections
操作集合的工具类
作业
- 第一题
- 需求: 创建学生类, 定义姓名和年龄属性, 创建多个学生对象, 根据学生的年龄进行排序
- 第二题
- 需求: 从键盘接收一个字符串, 程序对其中所有字符进行排序(重复不重复无所谓啦)
- 示例: 键盘输入: helloitcast程序打印:acehillostt
- 扩展题
- 第一题
- 在一个集合中存储了无序并且重复的字符串,定义一个方法,让其有序(字典顺序),而且还不能去除重复
- 使用TreeSet进行排序
- 第二题
- 思考, 能不能将HashSet集合可以存储重复元素(同一个对象)
- 第一题