一. 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集合中元素保证唯一性
-
原理解析
- HashMap是通过调用元素的hashCode方法来比较两个元素是否形同的,所有如果要保证引用数据类型逻辑上的唯一性,就必须重写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<T> list) 根据元素的自然顺序排序
- swap(List<T> list , int i , int j) 交换集合中两个角标位上的值
- reverse(List<?> list ) 反转集合中的元素的顺序
- replaceAll(List<T> 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
- 去重
- 无序(集合无序)
- HashSet
- 去重效率高, 尤其是在大数据量下
- 调用元素的hashCode和equals方法来比较是否相同
- LinkedHashSet
- 即有序(集合有序), 又能去重
- 效率不高
- TreeSet
- 去重, 元素有序(对存入的元素进行排序)
- 要求存入的元素必须具备比较的能力或者提供第三方的比较器
- 元素具备比较的能力 : 元素实现comperable接口, 重写comparTo方法
- 第三方比较器: 定义类,实现Compartor接口, 从写 compare方法
- 超级for
- 迭代器的简写形式
- 优点: 格式简单
- 缺点: 无法进行删除操作
- Collections
- 一个用来操作List集合的工具类
作业
-
第一题
- 需求: 创建学生类, 定义姓名和年龄属性, 创建多个学生对象, 根据学生的年龄进行排序
package com.huwenlong.day14; //方法一 import java.util.Comparator; import java.util.TreeSet; //使用TreeSet和第三方比较器对学生的年龄进行比较 public class Test01 { public static void main(String[] args) { TreeSet<Student> treeSet = new TreeSet<>(new Comparator<Student>() { @Override public int compare(Student o1, Student o2) { return o1.getAge()-o2.getAge(); } }); treeSet.add(new Student("小红",19)); treeSet.add(new Student("小明",20)); treeSet.add(new Student("小王",10)); treeSet.add(new Student("小赵",7)); for (Student student : treeSet) { System.out.println(student); } } } package com.huwenlong.day14; import java.util.Objects; public class Student { private String name; private int age; public Student(String name, int age) { this.name = name; this.age = age; } @Override public 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 && Objects.equals(name, student.name); } @Override public String toString() { final StringBuilder sb = new StringBuilder("Student{"); sb.append("name='").append(name).append('\''); sb.append(", age=").append(age); sb.append('}'); return sb.toString(); } @Override public int hashCode() { return Objects.hash(name, age); } public String getName() { return name; } public int getAge() { return age; } }
package com.huwenlong.day14; import java.util.LinkedList; //方法二:使用LinkedList并使用冒泡排序 public class TestDemo06 { public static void main(String[] args) { LinkedList<Student> linkedList = new LinkedList<>(); linkedList.add(new Student("小红",19)); linkedList.add(new Student("小明",20)); linkedList.add(new Student("小王",10)); linkedList.add(new Student("小赵",7)); for (int i = 0; i < linkedList.size()-1; i++) { for (int j = 0; j < linkedList.size()-1-i; j++) { Student s1 = linkedList.get(j); Student s2 = linkedList.get(j+1); if(s1.getAge()>s2.getAge()){ Student t = s1; linkedList.set(j,s2); linkedList.set(j+1,t); } } } for (Student student : linkedList) { System.out.println(student); } } }
-
第二题
- 需求: 从键盘接收一个字符串, 程序对其中所有字符进行排序(重复不重复无所谓啦)
- 示例: 键盘输入: helloitcast程序打印:acehillostt
package com.huwenlong.day14; //第一种方法:使用StringBuilder,可以处理重复字符 import java.util.Scanner; public class TestDemo07 { public static void main(String[] args) { Scanner sc = new Scanner(System.in); System.out.println("请输入一个字符串:"); String str = sc.next(); StringBuilder sb = new StringBuilder(str); for (int i = 0; i <sb.length()-1 ; i++) { for (int j = 0; j < sb.length()-1-i; j++) { if(sb.charAt(j)>sb.charAt(j+1)){ char ch = sb.charAt(j); sb.setCharAt(j,sb.charAt(j+1)); sb.setCharAt(j+1,ch); } } } System.out.println(sb.toString()); } }
package com.huwenlong.day14; //第二种方式:使用TreeNode,不能处理重复字符 import java.util.Scanner; import java.util.TreeSet; public class Test02 { public static void main(String[] args) { Scanner sc = new Scanner(System.in); System.out.println("请输入一个字符串:"); String str = sc.next(); TreeSet<Character> treeSet = new TreeSet<>(); for (int i = 0; i < str.length(); i++) { treeSet.add(str.charAt(i)); } for (Character character : treeSet) { System.out.print(character); } } }
package com.huwenlong.day14; import java.util.ArrayList; import java.util.Collections; import java.util.Scanner; //使用Collections.sort方法进行排序,可以处理重复字符 public class Test08 { public static void main(String[] args) { Scanner sc = new Scanner(System.in); String str = sc.next(); ArrayList<Character> list = new ArrayList<>(); for (int i = 0; i < str.length(); i++) { list.add(str.charAt(i)); } Collections.sort(list); for (Character character : list) { System.out.print(character); } } }
-
扩展题
-
第一题
- 在一个集合中存储了无序并且重复的字符串,定义一个方法,让其有序(字典顺序),而且还不能去除重复
- 使用TreeSet进行排序
package com.huwenlong.day14; import java.util.ArrayList; import java.util.TreeSet; public class Test03 { public static void main(String[] args) { ArrayList<String> list = new ArrayList<>(); list.add("abcdef"); list.add("poiuy"); list.add("abcdef"); list.add("lkjghf"); list.add("xxzxsd"); list.add("poiuy"); ArrayList<String> newList = getSortedString(list); for (String s : newList) { System.out.println(s); } } //首先将ArrayList中的内容传入到TreeSet中,去重 public static ArrayList<String> getSortedString(ArrayList<String> list){ TreeSet<String> set = new TreeSet<>(list); ArrayList<String> newList = new ArrayList<>(); //然后对于去重后的元素,计算出在原集合中出现的次数,再根据次数存到新字符串中 for (String s : set) { int count = 0; while(true){ int index = list.indexOf(s); if(index == -1) break; count++; list.remove(index); } for (int i = 0; i < count; i++) { newList.add(s); } } return newList; } }
-
-
第二题
- 思考, 能不能将HashSet集合可以存储重复元素(同一个对象)
package com.huwenlong.day14; import java.util.HashSet; public class Test04 { public static void main(String[] args) { HashSet<Student> set = new HashSet<>(); Student stu = new Student("小红",20); set.add(stu); System.out.println(set); set.add(stu); System.out.println(set); } } package com.huwenlong.day14; import java.util.Objects; public class Student { private String name; private int age; //自定义一个count属性通过构造方法初始化为0,然后重写hashCode方法,每次调用hashCode都会将count++,根据name age count三个属性计算哈希值,这样同一对象的前后hash值不一样就可以存储同一个对象了 private int count; public Student(String name, int age) { this.name = name; this.age = age; count = 0; } @Override public 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 && Objects.equals(name, student.name); } @Override public String toString() { final StringBuilder sb = new StringBuilder("Student{"); sb.append("name='").append(name).append('\''); sb.append(", age=").append(age); sb.append('}'); return sb.toString(); } @Override public int hashCode() { count++; return Objects.hash(name, age ,count); } public String getName() { return name; } public int getAge() { return age; } }
网友评论