集合非常的重要,在开发中使用的非常之多,是重点中的重点。
1、集合概述 1.1、什么是集合数组其实就是一个集合。集合实际上就是一个容器。可以用来容纳其他类型的数据。
集合在开发中使用的非常多,因为集合本身就是一个容器,是一个载体,可以一次容纳多个对象。在实际开发中,假设连接数据库,数据库当中有10条记录,那么假设把这十条记录查询出来,Java程序会把这十条数据封装到10个Java对象,然后将10个对象放到某个一个集合当中,再将这个集合传到前端,使用遍历的方式将数据展现出来。
集合不能直接存储基本数据类型,另外集合也不能直接存储Java对象,集合当中存储的都是对象的内存地址。(或者说集合中存储的是引用)
list.add(100); // 自动装箱Integer
注意:集合在Java中本身是一个容器,是一个对象,其中的任何存储的都是“引用”。
在Java中每一个不同的集合,底层会对应不同的数据结构。往不同的集合中存储元素,等于将数据放到了不同的数据结构中。什么是数据结构?数据存储的结构就是数据结构,不同的数据结构数据存储的方式不同。例如:数组、二叉树、链表、哈希表……
new ArrayList(); // 创建一个集合,底层是数组 new linkedList(); // 创建一个集合对象,底层是链表 new TreeSet(); // 创建一个集合对象,底层是二叉树1.2、在Java中集合分为两大类
一类是以单个方式存储元素
单个方式存储元素,这一类集合中超级父接口:java.util.Collection
一类是以键值对的方式存储元素
以键值对的方式存储元素,这一类集合中超级父接口:java.util.Map
1.3、Java集合继承的体系Java中所有的集合类和接口都在java.util.*包下。
1.3.1 Collection集合继承结构图: 1.3.2 Map集合继承结构图: 1.3.3 实现类总结- ArrayList:底层是数组。
- linkedList:底层是双向链表。
- Vector:底层是数组,线程安全的,效率较低,使用较少。
- HashSet:底层是HashMap,放到HashSet集合中的元素等同于放到HashMap集合key部分了。
- HashMap:底层是哈希表。
- Hashtable:底层也是哈希表,只不过线程是安全的,效率较低,使用较少。
- Properties:是线程安全的,并且key和value只能存储字符串String。
- TreeMap:底层是二叉树。TreeMap集合的key可以自动按照大小顺序排序。
List集合存储元素的特点:
有序可重复
有序:存进去的顺序和取出来的顺序相同,每一个元素有下标。
可重复:存进去1,可以在存储一个1.
Set(Map)集合存储元素的特定:
无序不可重复
无序:存进去的顺序和取出来的顺序不一定相同。另外Set集合中元素没有下标。
不可重复:存进去1,不能在存储1了。
SortedSet(SortedMap)存元素的特点:
首先是无序不可重复的,但是SortedSet集合中的元素是可排序的。
无序:存进去的顺序和取出来的顺序不一定相同。另外Set集合中元素没有下标。
不可重复:存进去1,不能在存储1了。
可排序:可以按照大小顺序排序。
Map集合的key,就是一个Set集合。
往Set集合中放数据,实际上放到了Map集合的Key部分。
1.4、Collection集合中常用方法package javase.collection;
import java.util.ArrayList;
import java.util.Collection;
public class CollectionTest01 {
public static void main(String[] args) {
// 创建一个集合对象
// Collection c =new Collection(); // 接口是抽象类,无法实例化
// 多态
Collection c = new ArrayList();
// 测试Collection接口中的常用方法
c.add(200); // 自动装箱(java5新特性),实际上放进去了一个对象的内存地址。即new Integer(200);
c.add(3.14); // 自动装箱
c.add(new Object());
c.add(new Student());
c.add(true); // 自动装箱
// 获取集合中元素的个数
System.out.println("集合中元素个数是 : " + c.size()); // 5
// 清空c中的元素
c.clear();
System.out.println("集合中元素个数是 : " + c.size()); // 0
// 向集合中添加元素
c.add("hello"); // "hello"对象的内存地址放到了集合当中。
c.add("world");
c.add("java");
c.add(1);
// 判断 c 中是否包含某个对象
System.out.println(c.contains("java")); // true
System.out.println(c.contains("php")); // false
System.out.println(c.contains(1)); // true
// 删除 c 中的1
c.remove(1);
System.out.println(c.contains(1)); // false
// 清空元素
c.clear();
//添加元素
c.add("java");
c.add("php");
c.add("hello world");
// 转换为数组(了解,使用不多)
Object[] objs = c.toArray();
for (Object obj: objs){
// 遍历数组
System.out.println(obj); // 输出引用时,自动调用toString()方法
}
}
}
class Student{}
1.5、集合迭代Iterator
注意:集合结构只要发生改变,迭代器必须重新获取。
package javase.collection;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
public class CollectionTest02 {
public static void main(String[] args) {
// 注意:以下讲解的遍历方式/迭代方式,是所有Collection通用的一种方式,
// 但是在Map集合中不能使用,在所有的Collection以及子类中使用。
// 创建集合对象
// ArrayList 有序可重复
Collection c = new ArrayList();// 后面的集合无所谓,主要是看前面的Collection接口怎么迭代。
// 添加元素
c.add("abc");
c.add("def");
c.add(100);
c.add(new Object());
// 对集合Collection进行迭代
// 第一步:获取集合对象的迭代对象Iterator
Iterator iterator = c.iterator();
// 第二步:通过以上获取的迭代器对象开始迭代
while (iterator.hasNext()){ // 如果这里iterator.hasNext()写成true会出现:java.util.NoSuchElementException
// 不管当初存进去的是什么,取出来统一都是Object
System.out.println(iterator.next());
}
}
}
1.6、contains()
资源地址:https://www.bilibili.com/video/BV1Rx411876f?p=673&spm_id_from=pageDriver
package javase.collection;
import java.util.ArrayList;
import java.util.Collection;
public class CollectionTest04 {
public static void main(String[] args) {
// 创建集合
Collection users = new ArrayList();
// 创建两个User对象
User u1 = new User("jack");
User u2 = new User("jack");
// 将 u1 加入users 集合
users.add(u1);
// 没有重写equals()方法之前
// System.out.println(users.contains(u2)); // false
// 重写equals()方法之后
// 由于u1和u2的username都是"jack",即它们在堆中保存的内存地址是相同的,由于User类的equals方法已经重写了。equals在判断的时候返回true。
// 而contains()方法底层调用的是equals,由于equals返回true,故contains()方法也返回true。
System.out.println(users.contains(u2)); // true
// 例如
Integer x = new Integer(1000);
users.add(x); // 将x加入到users集合中
Integer y = new Integer(1000);
// 判断user集合中是否包含y
System.out.println(users.contains(y)); // true
}
}
class User{
private String username;
public User(){}
public User(String username){
this.username = username;
}
// 重写equals()方法
// 将来调用equals()方法的时候,一定是调用这个重写了的equals()方法。
// 这个equals()方法的比较原理是:只要姓名一样同一个用户。
@Override
public boolean equals(Object o) {
if (o == null) return false;
if (this == o) return true;
if (!(o instanceof User)) return false;
User user = (User) o;
// username = "jack" , user.username = "jack"
// return Objects.equals(username, user.username); // idea 生成
// 这里调用String中已经重写了的equals()方法
return this.username.equals(user.username);
}
}
1.7、remove()
package javase.collection;
import java.util.ArrayList;
import java.util.Collection;
public class CollectionTest05 {
public static void main(String[] args) {
Collection cc = new ArrayList();
// 加入两个"hello"字符串对象
String s1 = "hello";
String s2 = "hello";
cc.add(s1); // s1加入集合cc
System.out.println(cc.size()); // 1
// 这里的s2并没有加入cc中,但是remove()底层还是会调用equals()方法,Java在执行equals()方法的时候认为s1与s2是相同的。
// 所以在删除s2的时候实际上也将s1删除了。
cc.remove(s2); // 删除s2
System.out.println(cc.size()); // 0
}
}
1.8、集合中元素的删除
package javase.collection;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
public class CollectionTest06 {
public static void main(String[] args) {
// 创建集合
Collection c = new ArrayList();
// 注意:此时获取到的迭代器指向的是那时集合中没有元素状态下的迭代器。
// 一定要注意:集合结构只要发生改变,迭代器必须重新获取。
// 当集合结构发生了改变,迭代器没有重新获取时,调用next()方法时会出现:java.util.ConcurrentModificationException异常。
Iterator it = c.iterator();
// 添加元素
c.add(1); // Integer类型 , 即“自动装箱”
c.add(2);
c.add(3);
// 遍历
// 因为这里的it迭代器是在向集合中添加元素之前的状态,然而集合中添加元素之后,集合的状态改变了,但是it并没有更新,所以出现异常。
Collection c2 = new ArrayList();
c2.add("abc");
c2.add("def");
c2.add("xyz");
Iterator it2 = c2.iterator();
while (it2.hasNext()) {
Object obj = it2.next();
// 删除元素
// 在删除元素之后,集合的结构发生了变化,应该重新获取迭代器,但是循环下一次并没有重新获取迭代器,所以会出现异常。
// 出现异常的根本原因:集合中元素删除了,但是没有更新迭代器(迭代器不知道集合发生变化了)。
// c2.remove(); // 直接通过集合去删除元素,没有通知迭代器。
// 使用迭代器来删除可以吗?
// 迭代器去删除时,会自动更新迭代器和集合(即也会删除集合中的元素)。
it2.remove(); // 迭代器删除的一定是迭代器指向的当前元素
System.out.println(obj);
}
System.out.println(c2.size()); // 0
}
}
2、List
2.1、List常用方法
package javase.List;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class ListTest01 {
public static void main(String[] args) {
// 创建List类型的集合
List list = new ArrayList();
// 添加元素
// add()方法默认在列表的最后添加元素,这个方法是用的比较多。
list.add("a");
list.add("b");
list.add("c");
list.add("d");
// 使用add(int index, Object element)添加元素,这个方法使用不多,因为对于ArrayList集合来说效率比较低。
list.add(1, "java"); // 在列表的第二个位置添加字符串"java"
// 迭代
Iterator it = list.iterator();
while (it.hasNext()) {
System.out.println(it.next());
}
// 根据下标获取元素
System.out.println(list.get(1)); // java
// 因为有下标,所以List集合有自己比较特殊的遍历方式,通过下标进行遍历。【List集合特有的方式,Set没有】
for (int i = 0; i < list.size(); i++) {
Object obj = list.get(i);
System.out.print(obj + " "); // a java b c d
}
System.out.println();
// 获取指定对象第一次出现处的索引
System.out.println(list.indexOf("java")); // 1
// 获取指定对象最后一次出现的索引
System.out.println(list.lastIndexOf("c")); // 3
// 删除集合中的指定元素
list.remove(1);
System.out.println(list.size()); // 4
// 用指定元素替换列表中指定位置的元素
list.set(1, "JavaSE");
for (Object o : list) {
System.out.print(o + " ");
}
}
}
2.2、ArrayList初始化及扩容
建议阅读一下jdk中的源代码。
package javase.ArrayList;
import java.util.ArrayList;
import java.util.List;
public class ArrayListTest01 {
public static void main(String[] args) {
// 默认初始化容量是 10
// 数组的长度是 10
List list1 = new ArrayList();
// 集合中的size()方法获取当前集合中元素的个数,不是获取集合的容量。
System.out.println(list1.size()); // 0
// 指定初始化容量,数组的长度指定为20
List list2 = new ArrayList(20);
System.out.println(list2.size()); // 0
}
}
2.3、linkedList
2.3.1、单向链表
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-w5xDZZDf-1635924321169)(https://gitee.com/zh0u9527/cloud_images/raw/master/img/20211031112007.png)]
代码实现单向链表:
link.java
package javase.singlelink;
public class link {
// 头结点
private Node header = null;
// 链表元素的个数
private int size = 0;
// 统计列表元素的个数
public int size(){
return size;
}
// 向链表中添加元素的方法(在末尾添加)
public void add(Object data){
// 创建一个新的节点对象,让之前单链表的末尾节点next指向新节点对象。
// 有可能这个元素是第一个,也可能是第二个,也可能是第三个。
if (header == null){
// 说明还没有节点
// new 一个新的节点对象,作为头节点对象
header = new Node(100, null);
} else {
// 说明头节点不为空,头结点已经存在了。找出当前末尾节点,让当前末尾节点的next是新节点。
Node currentLastNode = findLast(header);
currentLastNode.next = new Node(data, null);
}
size++; // 每添加一个元素 size加1
}
// 使用递归的方式来查找最后一个节点
private Node findLast(Node node) {
if (node.next == null){ // 如果一个节点的next为null,这说明当前这个节点就是最后一个节点。
return node;
}
// 程序能够到这里说明当前的node节点不是最后一个节点。
return findLast(node.next);
}
}
// 节点类
// 每一个节点都有两个属性,一个用来保存数据,另一个用来保存下一个节点的内存地址(也是节点)
class Node{
// 数据
Object data;
// 下一个节点
Node next;
public Node(){}
public Node(Object data, Node next){
this.data = data;
this.next = next;
}
}
Test.java
package javase.singlelink;
public class Test {
public static void main(String[] args) {
link link = new link();
link.add(100);
link.add(200);
link.add(300);
System.out.println(link.size()); // 3
}
}
2.3.2、双向链表
视频资源:https://www.bilibili.com/video/BV1Rx411876f?p=686&spm_id_from=pageDriver
2.3.3、linkedList原理package javase.linkedList;
import java.util.ArrayList;
import java.util.linkedList;
import java.util.List;
public class linkedListTest01 {
public static void main(String[] args) {
// linkedList集合底层也是有下标的。
// 注意:ArrayList之所以检索效率标记高,不是单纯因为下标的原因,还有底层数组发挥的主要作用。
// linkedList集合照样有下标,但是检索某个元素的时候效率比较低,因为只能从头节点开始一个一个遍历。
List list = new linkedList();
list.add("a");
list.add("b");
list.add("c");
list.add("d");
// 通过下标遍历linkedList中的元素
for (int i = 0; i < list.size(); i++) {
Object o = list.get(i);
System.out.print(o + " ");
}
System.out.println();
// linkedList集合有初始化容量吗?答案:没有。
// 最初这个链表中没有任何元素,first和last引用都是null,不管是linkedList还是ArrayList,以后写代码时不需要关心具体是那个集合。
// 因为我们是面向接口编程,调用的方法都是接口中的方法。
// List list1 = new ArrayList(); // 这样写表示底层使用了数组。
List list1 = new linkedList(); // 这样写表示底层使用了双向链表。
list1.add("abc");
list1.add("def");
list1.add("xyz");
list1.add("java");
for (int i = 0; i < list1.size(); i++) {
System.out.print(list1.get(i) + " ");
}
System.out.println();
}
}
2.4、Vector
java.util.Collections包下面有一个synchronizedList()方法可以将非线程安全的ArrayList集合转变为线程安全的ArrayList集合。
package javase.Vector;
import java.util.*;
public class VectorTest01 {
public static void main(String[] args) {
// 创建Vector对象
List list = new Vector();
// 添加元素
// vector初始化的默认容量是:10
list.add(1);
list.add(2);
list.add(3);
list.add(4);
list.add(5);
list.add(6);
list.add(7);
list.add(8);
list.add(9);
list.add(10);
// vector扩容,扩容之后是:20
list.add(11);
// Vector遍历
Iterator iterator = list.iterator();
while (iterator.hasNext()) {
System.out.print(iterator.next() + " "); // 1 2 3 4 5 6 7 8 9 10 11
}
System.out.println();
// 将一个ArrayList非线程安全的转换为Vector线程安全的?可以使用java.util.Collections类中的方法进行转换。
// 创建ArrayList对象
List list1 = new ArrayList(); // 非线程安全的。
// 将ArrayList转为Vector,变成线程安全的。
Collections.synchronizedList(list1);
// list1集合就是线程安全的了。
list1.add(10);
list1.add(20);
list1.add(30);
list1.add(40);
}
}
3、泛型
泛型属于编译阶段的功能,与运行阶段的关系不大。
3.1、泛型的概述package javase.generic;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class GenericTest01 {
public static void main(String[] args) {
// 未使用“泛型”
// 使用JDK5.0之后的新特性“泛型机制”后,代码变得更为直观
// 使用泛型List之后,表示List集合中只运行存储Animal类型的数据。
// 用泛型来指定集合中存储的数据类型。
List animals = new ArrayList();
// 指定List集合中指定存储Animal,如果存储其他类型如String的编译器就会报错。
// 这样用了泛型之后,集合中元素的数据类型更加统一了。
Cat cat = new Cat();
Bird bird = new Bird();
// 指定了泛型的集合中,可以存储器类型的子类。
animals.add(cat);
animals.add(bird);
// 获取迭代器,迭代器也可以指定其迭代的类型。
// 这个表示迭代器迭代的是Animal类型。如果不指定,则返回的默认还是“Object”类型。
Iterator it = animals.iterator();
while (it.hasNext()) {
// 使用泛型之后,每一次迭代返回的数据都是Animal类型
Animal animal = it.next();
// 这里不需要进行强制类型转换了,可以直接使用。
animal.move(); // 动物在移动!
// 但是这里如果是调用子类型中特有的方法时还是需要向下转型的。
if (animal instanceof Cat){
((Cat) animal).catchMouse(); // 猫爪老鼠!
}
}
}
}
class Animal{
public void move(){
System.out.println("动物在移动!");
}
}
class Cat extends Animal{
public void catchMouse(){
System.out.println("猫抓老鼠!");
}
}
class Bird extends Animal{
public void fly(){
System.out.println("鸟儿飞翔!");
}
}
3.2、钻石表达式
package javase.generic;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class GenericTest02 {
public static void main(String[] args) {
// ArrayList<这里的类型可以自动推断>();前提是JDK8之后才运行的。
// 自动类型推断
List myList = new ArrayList<>(); // ArrayList<>这里尖括号里面的类型可以不用写了(JDK8之后)。
myList.add("java");
myList.add("php");
myList.add("python");
// 不允许添加其他类型的数据
// myList.add(new Animal()); // 报错
// 获取迭代器对象,并指定迭代器类型为String
Iterator iterator = myList.iterator();
while (iterator.hasNext()) {
System.out.print(iterator.next() + " "); // java php python
}
System.out.println();
}
}
3.3、自定义泛型
package javase.generic; // 这里的3.4、增强版for循环是随便写的,只要符合标识符命名规则即可。 public class GenericTest03 { public void doSome(Test e){ // 这里的e也只是一个表标识符而已 System.out.println(e); } public static void main(String[] args) { // new对象的时候确定了泛型的对象,如:String类型 GenericTest03 gt = new GenericTest03<>(); // 上面指定了gt泛型的类型为String类型,如果我们传入其他类型编译器会报错。如:int // gt.doSome(100); // 类型不匹配 // 传入一个String类型的数据,编译器不会报错 gt.doSome("java is the best language!"); // 再如:这里指定泛型的类型为Integer的类型。 GenericTest03 igt = new GenericTest03<>(); // 传入一个100(会自动装箱)编译器不会报错。 igt.doSome(100); // 传入一个字符串类型编译器会报错 // igt.doSome("java"); // 类型不匹配 // 如果不使用泛型,默认就是Object类型 // GenericTest03 igt2 = new GenericTest03(); // igt2.doSome(new Object()); } } // 自定义一个类,并指定为泛型 class MyGeneric { public T getType(T j){ return null; } }
package javase.generic;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
// JDK5.0之后推出一个新特性,叫做增强for循环,或叫做foreach循环
public class ForeachTest01 {
public static void main(String[] args) {
// 定义一个数组
int[] array = {1, 2, 4, 6, 7};
// 普通for循环的遍历方式
for (int i = 0; i < array.length; i++) {
System.out.print(array[i] + " ");
}
System.out.println();
// 增强版for循环(又称为“foreach”循环)
// foreach有个小小的缺点:就是没有下标。
for (int i : array) {
System.out.print(i + " ");
}
System.out.println();
// 创建集合
List strings = new ArrayList<>();
strings.add("hello");
strings.add("world");
strings.add("javaSE");
// 集合的几种遍历方式
// 第一种使用迭代器遍历
Iterator iterator = strings.iterator();
while (iterator.hasNext()) {
System.out.print(iterator.next() + " "); // hello world javaSE
}
System.out.println();
// 第二种使用下标(针对有下标的集合)
for (int i = 0; i < strings.size(); i++) {
System.out.print(strings.get(i) + " "); // hello world javaSE
}
// 第三种是有foreach
for (String string : strings) { // 这里为什么是String string?因为我们上面定义strings的泛型类型为String类型。
System.out.print(string + " "); // // hello world javaSE
}
}
}
3.5、HashSet与TreeSet简单演示
HashSet:
package javase.set;
import java.util.HashSet;
public class HashSetTest01 {
public static void main(String[] args) {
// HashSet集合特点
HashSet strs = new HashSet<>();
// 添加元素
strs.add("hello2");
strs.add("hello1");
strs.add("hello3");
strs.add("hello3");
strs.add("hello3");
// 遍历
for (String str : strs) {
// 输出:hello1 hello2 hello3
// 1、存储时顺序和取出的顺序不同
// 2、不可重复
// 3、放到HashSet集合中的元素实际上是放到HashMap结合的key部分了。
System.out.print(str + " ");
}
}
}
TreeSet:
package javase.set;
import java.util.Set;
import java.util.TreeSet;
public class TreeSetTest01 {
public static void main(String[] args) {
// 创建集合对象
Set strs = new TreeSet<>();
// 添加元素
strs.add("a");
strs.add("b");
strs.add("z");
strs.add("y");
strs.add("k");
strs.add("z");
// 遍历
// 输出:a b k y z , 从小到大自动排序
for (String str : strs) {
System.out.print(str + " ");
}
}
}
4、Map集合
4.1、Map集合常用方法
package javase.map;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
public class MapTest01 {
public static void main(String[] args) {
Map map = new HashMap<>();
// 向Map集合中添加键值对
map.put(1, "龙小萌"); // 这里的 1 会自动装箱,和new Integer(1);是一样的。
map.put(2, "小萌狗");
map.put(3, "龙萌萌");
// 通过key获取value以及Map的遍历
for (Integer integer : map.keySet()) {
System.out.println(integer + ":" + map.get(integer));
}
// 获取Map集合中键值对的数量
System.out.println("键值对的数量: " + map.size()); // 3
// 通过key删除value
map.remove(3);
System.out.println("键值对的数量: " + map.size()); // 2
// 获取所有的value
Collection values = map.values();
for (String value : values) {
System.out.print(value + " ");
}
System.out.println();
// 判断是否包含某个key
// 需要注意的是,contains()方法底层调用的都是equals()方法进行比较的,所以自定义类都需要重写equals()方法。
System.out.println(map.containsKey(2)); // true
// 判断是否包含某个value
System.out.println(map.containsValue("小萌狗")); // true
// 清空Map集合
map.clear();
System.out.println("键值对的数量: " + map.size()); // 0
// 判断Map集合中的键值对是否为0
System.out.println(map.isEmpty()); // true
}
}
4.2、Map集合的遍历
常用两种方法进行遍历。
package javase.map;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
public class MapTest02 {
public static void main(String[] args) {
// 第一种方式,获取所有的key,通过遍历key来遍历value
Map map = new HashMap<>();
map.put(1, "zhangsan");
map.put(2, "lisi");
map.put(3, "wangwu");
map.put(4, "zhaoliu");
// 遍历Map集合
// 获取所有的key,所有的key是一个Set集合
Set keySet = map.keySet();
// 遍历key,通过key获取value
// 使用迭代器来实现
Iterator iterator = keySet.iterator();
while (iterator.hasNext()) {
// 取出其中一个key
Integer key = iterator.next();
// 通过key获取value
String value = map.get(key);
System.out.println(key + "=" + value);
}
// foreach来实现
for (Integer key : keySet) {
System.out.println(key + " = " + map.get(key));
}
// 第二种方式,Set> entrySet()
// 以上这个方法是把Map集合直接全部转换成Set集合,Set集合中元素的类型是:Map.Entry
Set> entrySet = map.entrySet();
// 遍历Set集合,每一次取出一个Node
// 这里使用迭代器来遍历
Iterator> it2 = entrySet.iterator();
while (it2.hasNext()) {
Map.Entry node = it2.next();
Integer key = node.getKey();
String value = node.getValue();
System.out.println(key + " = " + value);
}
// 增强for循环
// [这一种方式的效率要高一点],适用有大数据量的情况
for (Map.Entry stringEntry : entrySet) {
System.out.println(stringEntry.getKey() + " = " + stringEntry.getValue());
}
}
}
4.3、HashMap中两个重要的方法
map.put(k,v); v = map.get(k);
这两个方法应该重点掌握其原理。
package javase.map.HashMap;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
public class HashMapTest01 {
public static void main(String[] args) {
// 测试Hashmap集合key部分的元素特点
// Integer是key,它的hashCode和equals方法都已经重写了
Map map = new HashMap<>();
map.put(1111, "zhangsan");
map.put(2222, "lisi");
map.put(3333, "wangwu");
map.put(4444, "zhaoliu");
map.put(4444, "zh0u"); // key重复的时候value会自动覆盖
System.out.println(map.size()); // 4
// 遍历Map集合
Set> entrySet = map.entrySet();
for (Map.Entry entry : entrySet) {
// 验证结果:HashMap集合key部分元素:无序不可重复。
System.out.println(entry.getKey() + " -> " + entry.getValue());
}
}
}
4.4、重写hashCode()方法
package javase.map.HashMap;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
public class HastMapTest02 {
public static void main(String[] args) {
Student s1 = new Student("zhangsan");
Student s2 = new Student("zhangsan");
// 重写equals()方法之前
// System.out.println(s1.equals(s2)); // false
// 重写equals()方法之后
System.out.println(s1.equals(s2)); // true
System.out.println("s1的hashCode = " + s1.hashCode());// 1163157884 (hashCode()方法重写之后:-1432604525)
System.out.println("s2的hashCode = " + s2.hashCode());// 1956725890 (hashCode()方法重写之后:-1432604525)
// s1.equals(s2);结果已经是true了,表示s1和s2是一样的,相同的,那么往HashSet集合中放元素,理论上来说这里应该只能放进去一个,
// HashSet集合的特点:无序不可重复。
Set students = new HashSet<>();
students.add(s1);
students.add(s2);
// hashCode()方法重写之前,理论上来说这里输出的应该是1,但是结果去输出的是2,显然不符合HashSet集合存储元素的特点。
System.out.println(students.size());
// 重写Student类当中的hashCode()方法之后
System.out.println(students.size()); // 1
}
}
// 学生类
class Student{
private String username;
public Student() {
}
public Student(String username) {
this.username = username;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Student)) return false;
Student student = (Student) o;
return Objects.equals(getUsername(), student.getUsername());
}
@Override
public int hashCode() {
return Objects.hash(getUsername());
}
}
4.5、补充
package javase.map.HashMap;
import java.util.HashMap;
import java.util.Map;
public class HashMapTest03 {
public static void main(String[] args) {
Map map = new HashMap();
map.put(null, null);
// 通过key获取到value
System.out.println(map.get(null)); // null
// 输出map中元素的个数
System.out.println(map.size()); // 1
// 值覆盖
map.put(null, 100);
System.out.println(map.get(null)); // 100
}
}
4.6、Hashtable
package javase.map.Hashtable;
import java.util.Hashtable;
import java.util.Map;
public class HashtableTest01 {
public static void main(String[] args) {
Map map = new Hashtable();
// map.put(null, 123); // java.lang.NullPointerException
// map.put(1, null); // java.lang.NullPointerException
}
}
4.7、Properties
package javase.map.Hashtable;
import java.util.Properties;
public class PropertiesTest01 {
public static void main(String[] args) {
// 创建一个Properties对象
Properties properties = new Properties();
// 需要掌握Properties的两个方法,一个存,一个取。
properties.setProperty("url", "jdbc:mysql://localhost:3306/zh0u");
properties.setProperty("driver", "com.mysql.jdbc.Driver");
properties.setProperty("username", "root");
properties.setProperty("password", "123");
// 通过key获取value
String url = properties.getProperty("url");
String driver = properties.getProperty("driver");
String username = properties.getProperty("username");
String password = properties.getProperty("password");
System.out.println(url); // jdbc:mysql://localhost:3306/zh0u
System.out.println(driver); // com.mysql.jdbc.Driver
System.out.println(username); // root
System.out.println(password); // 123
}
}
4.8、TreeSet
package javase.map.TreeSet;
import java.util.TreeSet;
public class TreeSetTest01 {
public static void main(String[] args) {
// 创建一个TreeSet集合
TreeSet ts = new TreeSet<>();
// i添加字符串
ts.add("zhangsan");
ts.add("lisi");
ts.add("wangwu");
ts.add("zhangsi");
ts.add("wangliu");
// 遍历
for (String t : ts) {
// 按照字典顺序,升序
System.out.println(t);
}
// 创建一个Integer类型的TreeSet集合
TreeSet integers = new TreeSet<>();
integers.add(30);
integers.add(20);
integers.add(3);
integers.add(60);
integers.add(1);
// 遍历
for (Integer integer : integers) {
// 升序
System.out.println(integer);
}
}
}
4.9、自定义类型实现Comparable接口
package javase.map.TreeSet;
import java.util.TreeSet;
public class TreeSetTest02 {
public static void main(String[] args) {
Person p1 = new Person(32);
Person p2 = new Person(20);
Person p3 = new Person(30);
Person p4 = new Person(24);
// 创建集合
TreeSet personTreeSet = new TreeSet<>();
personTreeSet.add(p1);
personTreeSet.add(p2);
personTreeSet.add(p3);
personTreeSet.add(p4);
// 遍历
for (Person person : personTreeSet) {
// 输出引用数据类型时,会自动调用toString()方法。
System.out.println(person);
}
}
}
// 放在TreeSet集合汇总的元素需要实现java.lang.Comparable接口
// 并且实现compareTo()方法。equals()方法可以不写。
class Person implements Comparable {
private int age;
public Person(){
}
public Person(int age){
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"age=" + age +
'}';
}
// 需要在这个方法中编写比较的逻辑,或者说是比较规则,按照什么原理进行比较!
// k.compareTo(t.key);
// 拿着参数k和集合中的每一个k进行比较,返回值可能是大于零、小于零、等于零。
// 比较规则最终还是由程序员指定的:例如这里按照年龄升序。
@Override
public int compareTo(Person o) {
return this.age - o.age; // 升序
}
}
4.10 自定义排序规则
package javase.map.TreeSet;
import java.util.TreeSet;
// 先按照年龄升序,如果年龄一样的在按照姓名升序。
public class TreeSetTest03 {
public static void main(String[] args) {
TreeSet vips = new TreeSet<>();
vips.add(new Vip("zhangsi", 20));
vips.add(new Vip("zhangsan", 20));
vips.add(new Vip("king", 18));
vips.add(new Vip("ying", 17));
for (Vip vip : vips) {
System.out.println(vip);
}
}
}
class Vip implements Comparable {
private String username;
private int age;
public Vip(){}
public Vip(String username, int age){
this.username = username;
this.age = age;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Vip{" +
"username='" + username + ''' +
", age=" + age +
'}';
}
// 这里的排序规则为:先按照年龄进行排序,如果年龄一样。在按照姓名字典进行排序。
@Override
public int compareTo(Vip o) {
// 这里编写的就是放入Set集合中时,排序的规则。
if (this.age == o.age){
// String类型与Integer类型中的compareTo()方法已经实现了重写直接调用即可。
return this.username.compareTo(o.username);
} else {
return this.age - o.age;
}
}
}
4.11、自平衡二叉树数据结构
4.12、实现比较器接口
package javase.map.TreeSet;
import java.util.Comparator;
import java.util.TreeSet;
public class TreeSetTest04 {
public static void main(String[] args) {
// 创建TreeSet集合的时候,需要使用这个比较器。
// TreeSet wuGuis = new TreeSet<>(); // 这样不行,没有通过构造方法传递比较器
// 方式一:使用匿名内部类,直接new接口,然后在接口的后面加上{}
TreeSet wuGuis = new TreeSet<>(new Comparator() {
@Override
public int compare(WuGui o1, WuGui o2) {
return o1.getAge() - o2.getAge();
}
});
// 方式二:给构造方法传递一个比较器
// TreeSet wuGuis = new TreeSet<>(new WuGuiComparator());
// 添加元素
wuGuis.add(new WuGui(1000));
wuGuis.add(new WuGui(300));
wuGuis.add(new WuGui(150));
wuGuis.add(new WuGui(40));
wuGuis.add(new WuGui(800));
// 遍历
for (WuGui wuGui : wuGuis) {
System.out.println(wuGui);
}
}
}
// 创建乌龟对象
class WuGui{
private int age;
public WuGui(int age){
this.age = age;
}
@Override
public String toString() {
return "WuGui{" +
"age=" + age +
'}';
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
// 创建乌龟对象比较器,比较的规则是按照年龄进行升序。
class WuGuiComparator implements Comparator{
@Override
public int compare(WuGui o1, WuGui o2) {
return o1.getAge() - o2.getAge();
}
}
4.13、Collections工具类
package javase;
import java.util.*;
public class CollectionsTest01 {
public static void main(String[] args) {
// ArrayList集合是非线程安全的。
List stringList = new ArrayList<>();
// 变成线程安全的
Collections.synchronizedList(stringList);
// 添加元素
stringList.add("abf");
stringList.add("abx");
stringList.add("abc");
stringList.add("abe");
Collections.sort(stringList);
for (String s : stringList) {
System.out.println(s);
}
// 自定义数据类型
List userList = new ArrayList<>();
userList.add(new User(2, "zh0u"));
userList.add(new User(1, "margin"));
userList.add(new User(0, "alice"));
userList.add(new User(1, "java"));
// 注意:对List集合中元素进行排序,需要保证List集合中的元素实现了Comparable接口。
Collections.sort(userList);
for (User user : userList) {
System.out.println(user);
}
// 对Set集合怎么排序?
Set stringSet = new HashSet<>();
stringSet.add("java");
stringSet.add("php");
stringSet.add("python");
stringSet.add("mysql");
// 先将Set集合转换成List集合
List stringArrayList = new ArrayList<>(stringSet);
Collections.sort(stringArrayList);
// 遍历
for (String s : stringArrayList) {
System.out.println(s);
}
// 还有一种排序的方式
// Collections.sort(列表, 比较器);
}
}
class User implements Comparable{
private int id;
private String username;
public User(int id, String username) {
this.id = id;
this.username = username;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
// 重写Comparable接口中的compareTo()方法。
@Override
public int compareTo(User o) {
if (this.id == o.id) {
return this.username.compareTo(o.username);
} else {
return this.id - o.id;
}
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + ''' +
'}';
}
}
;
for (String s : stringList) {
System.out.println(s);
}
// 自定义数据类型
List userList = new ArrayList<>();
userList.add(new User(2, "zh0u"));
userList.add(new User(1, "margin"));
userList.add(new User(0, "alice"));
userList.add(new User(1, "java"));
// 注意:对List集合中元素进行排序,需要保证List集合中的元素实现了Comparable接口。
Collections.sort(userList);
for (User user : userList) {
System.out.println(user);
}
// 对Set集合怎么排序?
Set stringSet = new HashSet<>();
stringSet.add("java");
stringSet.add("php");
stringSet.add("python");
stringSet.add("mysql");
// 先将Set集合转换成List集合
List stringArrayList = new ArrayList<>(stringSet);
Collections.sort(stringArrayList);
// 遍历
for (String s : stringArrayList) {
System.out.println(s);
}
// 还有一种排序的方式
// Collections.sort(列表, 比较器);
}
}
class User implements Comparable{
private int id;
private String username;
public User(int id, String username) {
this.id = id;
this.username = username;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
// 重写Comparable接口中的compareTo()方法。
@Override
public int compareTo(User o) {
if (this.id == o.id) {
return this.username.compareTo(o.username);
} else {
return this.id - o.id;
}
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + ''' +
'}';
}
}



