模块19回顾
在深入探讨模块20之前,让我们回顾一下day19中的关键内容:
- Collection集合:单列集合的顶级接口,提供了
add
、addAll
、clear
、size
、isEmpty
、remove
、toArray
、contains
等方法。 - 迭代器(Iterator):通过
iterator
方法获取,提供了hasNext()
和next()
方法。在迭代集合时,不能随意修改集合长度,否则会抛出并发修改异常。 - 数据结构:栈(先进后出)、队列(先进先出)、数组(查询快,增删慢)、链表(查询慢,增删快)。
- ArrayList:特点包括元素有序、有索引、元素可重复、线程不安全。底层数据结构为数组,提供了
add
、add(index, element)
、remove
、size
、get
、set
等方法。自动扩容机制,扩容1.5倍。 - LinkedList:特点包括元素有序、有索引(通过方法支持,非本质索引)、元素可重复、线程不安全。底层数据结构为双向链表,提供了大量直接操作首尾元素的方法。
- 增强for循环:格式为
for(元素类型 变量名:集合名或者数组名)
,遍历集合时使用迭代器,遍历数组时使用普通for循环。
模块20重点
本模块将深入探讨集合的高级应用,包括:
- 掌握
Collections
集合工具类的常用方法。 - 掌握泛型的使用。
- 了解
HashSet
和LinkedHashSet
的特点及使用。 - 了解
HashSet
将元素去重的过程。
第一章:Collections集合工具类
Collections
是一个集合工具类,提供了多种静态方法来操作集合。
static <T> boolean addAll(Collection<? super T> c, T... elements)
:批量添加元素。static void shuffle(List<?> list)
:将集合中的元素顺序打乱。static <T> void sort(List<T> list)
:将集合中的元素按照默认规则排序。static <T> void sort(List<T> list, Comparator<? super T> c)
:将集合中的元素按照指定规则排序。
public class Demo01Collections {public static void main(String[] args) {ArrayList<String> list = new ArrayList<>();Collections.addAll(list, "张三", "李四", "王五", "赵六", "田七", "朱八");System.out.println(list);Collections.shuffle(list);System.out.println(list);ArrayList<String> list1 = new ArrayList<>();list1.add("c.举头望明月");list1.add("a.床前明月光");list1.add("d.低头思故乡");list1.add("b.疑是地上霜");Collections.sort(list1);System.out.println(list1);}
}
第二章:泛型
泛型是Java中用于统一数据类型、防止数据类型转换异常的一种机制。
public class Demo01Genericity {public static void main(String[] args) {ArrayList list = new ArrayList();list.add("1");list.add(1);list.add("abc");list.add(2.5);list.add(true);for (Object o : list) {String s = (String) o;System.out.println(s.length());}}
}
2.1 含有泛型的类
public class MyArrayList<E> {Object[] obj = new Object[10];int size;public boolean add(E e) {obj[size] = e;size++;return true;}public E get(int index) {return (E) obj[index];}@Overridepublic String toString() {return Arrays.toString(obj);}
}
2.2 含有泛型的方法
public class ListUtils {public static <E> void addAll(ArrayList<E> list, E... e) {for (E element : e) {list.add(element);}}
}
2.3 含有泛型的接口
public interface MyList<E> {boolean add(E e);
}
2.4 泛型的高级使用
3.1 泛型通配符 ?
public class Demo01Genericity {public static void main(String[] args) {ArrayList<String> list1 = new ArrayList<>();list1.add("张三");list1.add("李四");ArrayList<Integer> list2 = new ArrayList<>();list2.add(1);list2.add(2);method(list1);method(list2);}public static void method(ArrayList<?> list) {for (Object o : list) {System.out.println(o);}}
}
3.2 泛型的上限下限
public class Demo02Genericity {public static void main(String[] args) {ArrayList<Integer> list1 = new ArrayList<>();ArrayList<String> list2 = new ArrayList<>();ArrayList<Number> list3 = new ArrayList<>();ArrayList<Object> list4 = new ArrayList<>();get1(list1);//get1(list2);错误get1(list3);//get1(list4);错误System.out.println("=================");//get2(list1);错误//get2(list2);错误get2(list3);get2(list4);}public static void get1(Collection<? extends Number> collection) {}public static void get2(Collection<? super Number> collection) {}
}
第三章:斗地主案例(扩展案例)
3.1 案例介绍
按照斗地主的规则,完成洗牌发牌的动作。具体规则:使用54张牌打乱顺序,三个玩家参与游戏,三人交替摸牌,每人17张牌,最后三张留作底牌。
3.2 案例分析
- 准备牌:牌可以设计为一个
ArrayList<String>
,每张牌由花色数字两部分组成,使用Collections
类的shuffle
方法进行随机排序。 - 发牌:将每个人以及底牌设计为
ArrayList<String>
,将最后3张牌直接存放于底牌,剩余牌通过对3取模依次发牌。 - 看牌:直接打印每个集合。
3.3 代码实现
public class Poker {public static void main(String[] args) {ArrayList<String> color = new ArrayList<>();ArrayList<String> number = new ArrayList<>();ArrayList<String> poker = new ArrayList<>();color.add("♠");color.add("♥");color.add("♣");color.add("♦");for (int i = 2; i <= 10; i++) {number.add(i + "");}number.add("J");number.add("Q");number.add("K");number.add("A");for (String num : number) {for (String huaSe : color) {String pokerNumber = huaSe + num;poker.add(pokerNumber);}}poker.add("😊");poker.add("☺");Collections.shuffle(poker);ArrayList<String> p1 = new ArrayList<>();ArrayList<String> p2 = new ArrayList<>();ArrayList<String> p3 = new ArrayList<>();ArrayList<String> dipai = new ArrayList<>();for (int i = 0; i < poker.size(); i++) {String s = poker.get(i);if (i >= 51) {dipai.add(s);} else if (i % 3 == 0) {p1.add(s);} else if (i % 3 == 1) {p2.add(s);} else if (i % 3 == 2) {p3.add(s);}}System.out.println("涛哥:" + p1);System.out.println("三上:" + p2);System.out.println("金莲:" + p3);System.out.println("底牌:" + dipai);}
}
第四章:红黑树(了解)
集合加入红黑树的目的:提高查询效率。HashSet
集合的数据结构包括哈希表,其中JDK8之前为数组+链表,JDK8之后为数组+链表+红黑树,以提高查询效率。
-
每一个节点或是红色的,或者是黑色的
-
根节点必须是黑色
-
如果一个节点没有子节点或者父节点,则该节点相应的指针属性值为Nil,这些Nil视为叶节点,每个叶节点(Nil)是黑色的
-
如果某一个节点是红色,那么它的子节点必须是黑色(不能出现两个红色节点相连 的情况)
-
对每一个节点,从该节点到其所有后代叶节点的简单路径上,均包含相同数目的黑色节点
https://www.cs.usfca.edu/~galles/visualization/RedBlack
第五章:Set集合
Set接口并没有对Collection接口进行功能上的扩充,而且所有的Set集合底层都是依靠Map实现
1.Set集合介绍
Set
和Map
密切相关,Map
的遍历需要先变成单列集合,只能变成Set
集合。
2.HashSet集合的介绍和使用
HashSet
是Set
接口的实现类,具有元素唯一、无序、无索引、线程不安全等特点。底层数据结构为哈希表。
public class Demo01HashSet {public static void main(String[] args) {HashSet<String> set = new HashSet<>();set.add("张三");set.add("李四");set.add("王五");set.add("赵六");set.add("田七");set.add("张三");System.out.println(set);Iterator<String> iterator = set.iterator();while (iterator.hasNext()) {System.out.println(iterator.next());}for (String s : set) {System.out.println(s);}}
}
3.LinkedHashSet的介绍以及使用
LinkedHashSet
继承自HashSet
,具有元素唯一、有序、无索引、线程不安全等特点。底层数据结构为哈希表+双向链表。
public class Demo02LinkedHashSet {public static void main(String[] args) {LinkedHashSet<String> set = new LinkedHashSet<>();set.add("张三");set.add("李四");set.add("王五");set.add("赵六");set.add("田七");set.add("张三");System.out.println(set);Iterator<String> iterator = set.iterator();while (iterator.hasNext()) {System.out.println(iterator.next());}for (String s : set) {System.out.println(s);}}
}
4.哈希值
哈希值是由计算机算出来的一个十进制数,可以看做是对象的地址值。获取对象的哈希值使用的是Object
中的方法public native int hashCode()
。
注意:
a. 哈希值不一样,内容肯定不一样
b. 哈希值一样,内容也有可能不一样
public class Person {private String name;private Integer age;@Overridepublic boolean equals(Object o) {if (this == o) return true;if (o == null || getClass() != o.getClass()) return false;Person person = (Person) o;return Objects.equals(name, person.name) && Objects.equals(age, person.age);}@Overridepublic int hashCode() {return Objects.hash(name, age);}@Overridepublic String toString() {return "Person{" +"name='" + name + '\'' +", age=" + age +'}';}
}
public class Demo01Hash {public static void main(String[] args) {Person p1 = new Person("涛哥", 18);Person p2 = new Person("涛哥", 18);System.out.println(p1);System.out.println(p2);System.out.println(p1.hashCode());System.out.println(p2.hashCode());}
}
5.字符串的哈希值是如何算出来的
字符串的哈希值是通过特定的算法计算出来的,例如String
类的哈希算法。
public int hashCode() {int h = hash;if (h == 0 && !hashIsZero) {h = isLatin1() ? StringLatin1.hashCode(value): StringUTF16.hashCode(value);if (h == 0) {hashIsZero = true;} else {hash = h;}}return h;
}
6.HashSet的存储去重复的过程
HashSet
通过计算元素的哈希值和比较内容来去重。
public class Test02 {public static void main(String[] args) {HashSet<String> set = new HashSet<>();set.add("abc");set.add("通话");set.add("重地");set.add("abc");System.out.println(set);}
}
7.HashSet存储自定义类型如何去重复
自定义类型需要重写hashCode
和equals
方法来实现去重。
public class Person {private String name;private Integer age;@Overridepublic boolean equals(Object o) {if (this == o) return true;if (o == null || getClass() != o.getClass()) return false;Person person = (Person) o;return Objects.equals(name, person.name) && Objects.equals(age, person.age);}@Overridepublic int hashCode() {return Objects.hash(name, age);}@Overridepublic String toString() {return "Person{" +"name='" + name + '\'' +", age=" + age +'}';}
}public class Test03 {public static void main(String[] args) {HashSet<Person> set = new HashSet<>();set.add(new Person("涛哥", 16));set.add(new Person("金莲", 24));set.add(new Person("涛哥", 16));System.out.println(set);}
}
小结
通过本文的学习,希望能够帮助您深入理解集合的高级应用,包括Collections
集合工具类的常用方法、泛型的使用、HashSet
和LinkedHashSet
的特点及使用,以及HashSet
将元素去重的过程。这些都是Java集合框架中非常重要的知识点。