1.Collection
集合和数组的区别
- 集合
- 是一个容器, 用来存放多个数据
- 集合中的元素可以是不同数据类型
- 集合中只能存放引用数据类型, 但是如果将基本数据类型的数据存入集合是可以的, 因为进行自动装箱
- 集合的长度是可变的, 随着元素的增删, 改变集合的长度
- 数组
- 是一个容器, 用来存放多个数据
- 数组中的所有元素必须是同一种数据类型
- 数组中的数据类型可以是基本数据类型也可以是引用数据类型
- 数组的长度是固定的
单列集合的继承体系
Collection中的常用功能
方法如下:
public boolean add(E e)
: 把给定的对象添加到当前集合中 。public void clear()
:清空集合中所有的元素。public boolean remove(E e)
: 把给定的对象在当前集合中删除。public boolean contains(Object obj)
: 判断当前集合中是否包含给定的对象。public boolean isEmpty()
: 判断当前集合是否为空。public int size()
: 返回集合中元素的个数。public Object[] toArray()
: 把集合中的元素,存储到数组中
2.Iterator迭代器
迭代器的使用
-
获取迭代器对象
Collection: Iterator<E> iterator() : 获取集合对应的迭代器
-
判断是否有下一个元素
Iterator: boolean hasNext() : 判断是否有下一个元素 E next() : 获取下一个元素
使用迭代器进行遍历
for循环和while循环的使用:
- 明确循环次数 -> for
- 不明确循环次数 -> while
// 遍历集合
// 1. 获取集合对应的迭代器
Iterator<String> it = c.iterator();
// 2. 判断是否有下一个元素
while (it.hasNext()) {
// 3. 如果有就获取
String s = it.next();
System.out.println(s);
}
迭代器的原理
迭代器遍历自定义类型(Person)
// 先创建一个用来存放Person类型的集合
ArrayList<Person> list = new ArrayList<>();
// 存放Person对象 - 匿名对象
list.add(new Person("张三", 23));
list.add(new Person("李四", 24));
list.add(new Person("王五", 25));
list.add(new Person("赵六", 26));
// 获取集合对应的迭代器
Iterator<Person> it = list.iterator();
// 判断集合中是否有下一个元素
while (it.hasNext()) {
// 获取下一个元素 (Person)
// System.out.println(it.next().getName() + "---" + it.next().getAge());
// 使用上面方式的话, 获取到的数据有问题
// 解决方案:
Person p = it.next();
System.out.println(p.getName() + "---" + p.getAge());
}
3. 迭代器使用时的注意事项
(1) NoSuchElementException
- 迭代器遍历的过程中, 已经没有元素可以获取了, 仍然继续使用next()方法
(2) ConcurrentModificationException
- 并发修改异常
- 产生的原因: 使用迭代器遍历集合的同时, 使用集合的方法修改了集合(可用迭代器修改)
ArrayList<String> list = new ArrayList<>();
list.add("a");
list.add("b");
list.add("java");
list.add("c");
list.add("d");
// 迭代器遍历
Iterator<String> it = list.iterator();
// 判断是否有下一个元素
while (it.hasNext()) {
// 获取下一个元素
String s = it.next();
// 判断: 如果获取到的是java, 就删除
if ("java".equals(s)) {
// 使用集合的删除方法, 会出现并发修改异常
// list.remove(s);
// 使用迭代器的删除方法
it.remove();
}
}
System.out.println(list);
特殊情况, 不会出现并发修改异常:
要删除的元素, 在集合的倒数第二个位置
增强for循环
- 增强for循环: 用来遍历容器(集合, 数组)
格式
for (容器中元素的数据类型 变量名 : 容器) {
使用变量;
}
举例
Collection<Integer> c = new ArrayList<>();
c.add(1);
c.add(2);
c.add(3);
c.add(4);
c.add(5);
for (Integer i : c) {
System.out.println(i);
}
int[] arr = {10, 20, 30, 40, 50};
for (int i : arr) {
System.out.println(i);
}
快捷键
容器.for -> 回车
三种循环的应用场景
- 普通for循环 : 使用索引
- 迭代器 : 删除集合中元素, 使用迭代器自己的remove()
- 增强for:只做遍历
List集合
有序(不是排序) : 存和取的顺序是一致的
有索引 : 集合中的元素都是有索引的
可以重复 : 集合中可以存放相同的元素
1. List中的常用功能
- 增
boolean add(E e)
void add(int index, E e) : 向指定索引处, 插入元素
- 删
E remove(int index) : 删除指定索引处的元素
返回被删除的元素
- 改
E set(int index, E e) : 修改指定索引位置的元素
返回被修改的元素
- 查
E get(int index) : 获取指定索引处的元素
返回指定索引处的元素
2. List的子类
(1) ArrayList
- 底层数据结构是数组
(2) LinkedList
- 底层数据结构是链表(双向链表)
LinkedList特有功能, 都是针对头和为进行操作
// 头: first 尾: last
addFirst(E e);
addLast(E e);
removeFirst();
removeLast();
getFirst();
getLast();
pop();
push();
(3) Vector
- 底层数据结构是数组, 是线程安全的,已经被ArrayList取代了
(4) 模拟栈结构(面试题)
package com.itheima._04list;
import java.util.LinkedList;
/**
*
* 模拟栈结构
*
* 进栈
*
* 出栈
*
*/
public class MyStack<E> {
// 自己定义一个LinkedList对象
private LinkedList<E> list = new LinkedList<>();
/**
* 进栈功能
* @param e
*/
public void in(E e) {
list.push(e);
}
/**
* 出栈
*/
public E out() {
return list.pop();
}
/**
* 判断栈是否为空
* @return
*/
public boolean isEmpty() {
return list.isEmpty();
}
@Override
public String toString() {
return list.toString();
}
}
ArrayList
- 通过查看ArrayList类的API:
java.util.ArrayList <E>
:该类需要 import导入使后使用。- 表示一种指定的数据类型,叫做泛型,用于约束集合中存储元素的数据类型
- 怎么用呢?
- 在出现E的地方我们使用引用数据类型替换即可,表示我们将存储哪种引用类型的元素。
- 例如:
- ArrayList 表示ArrayList集合中只能存储String类型的对象
- ArrayList 表示ArrayList集合中只能存储Student类型的对象
- …
- 概述:
- ArrayList类底层是大小可变的数组的实现,存储在内的数据称为元素。也就是说ArrayList 中可以不断添加元素,其大小会自动增长。
ArrayList类构造方法
-
介绍ArrayList的构造方法
ArrayList()
构造一个初始容量为 10 的空列表,当真正对数组进行添加时,才真正分配容量,每次扩容是原来的1.5倍。
-
案例演示
ArrayList<String> list1 = new ArrayList<>();//创建一个ArrayList对象,集合中的元素类型为String类型 ArrayList<Student> list2 = new ArrayList<>();//创建一个ArrayList对象,集合中的元素类型为Student类型
ArrayList类添加元素方法
-
ArrayList添加元素的方法
- public boolean add(E e):将指定的元素追加到此集合的末尾
- public void add(int index,E element):在此集合中的指定位置插入指定的元素
-
案例演示:
public static void main(String[] args) { ArrayList<String> array = new ArrayList<String>(); //public boolean add(E e):将指定的元素追加到此集合的末尾 // System.out.println(array.add("hello")); array.add("hello"); array.add("world"); array.add("java"); //public void add(int index,E element):在此集合中的指定位置插入指定的元素 array.add(1,"javase"); // array.add(3,"javaee"); //IndexOutOfBoundsException // array.add(4,"javaee"); //输出集合 System.out.println("array:" + array); }
###ArrayList类常用方法
方法名 | 说明 |
---|---|
public boolean remove(Object o) | 删除指定的元素,返回删除是否成功 |
public E remove(int index) | 删除指定索引处的元素,返回被删除的元素 |
public E set(int index, E element) | 修改指定索引处的元素,返回被修改的元素 |
public E get(int index) | 返回指定索引处的元素 |
public int size() | 返回集合中的元素的个数 |
示例代码
public class ArrayListDemo02 {
public static void main(String[] args) {
//创建集合
ArrayList<String> array = new ArrayList<String>();
//添加元素
array.add("hello");
array.add("world");
array.add("java");
//public boolean remove(Object o):删除指定的元素,返回删除是否成功
// System.out.println(array.remove("world"));
// System.out.println(array.remove("javaee"));
//public E remove(int index):删除指定索引处的元素,返回被删除的元素
// System.out.println(array.remove(1));
//IndexOutOfBoundsException
// System.out.println(array.remove(3));
//public E set(int index,E element):修改指定索引处的元素,返回被修改的元素
// System.out.println(array.set(1,"javaee"));
//IndexOutOfBoundsException
// System.out.println(array.set(3,"javaee"));
//public E get(int index):返回指定索引处的元素
// System.out.println(array.get(0));
// System.out.println(array.get(1));
// System.out.println(array.get(2));
//System.out.println(array.get(3)); //?????? 自己测试
//public int size():返回集合中的元素的个数
System.out.println(array.size());
//输出集合
System.out.println("array:" + array);
}
}
ArrayList存储字符串并遍历
需求
- 创建一个存储字符串的集合,存储3个字符串元素,使用程序实现在控制台遍历该集合
分析
- 创建集合对象
- 往集合中添加字符串对象
- 遍历集合,首先要能够获取到集合中的每一个元素,这个通过get(int index)方法实现
- 遍历集合,其次要能够获取到集合的长度,这个通过size()方法实现
- 遍历集合的通用格式
实现
import java.util.ArrayList;
/*
ArrayList存储字符串并遍历
需求
- 创建一个存储字符串的集合,存储3个字符串元素,使用程序实现在控制台遍历该集合
分析:
1.创建ArrayList集合对象,限制集合中元素的类型为String类型
2.往集合对象中存储3个字符串元素 使用add()方法往集合中添加元素
3.获取集合中元素的个数
4.循环遍历集合中每一个元素
*/
public class Test1ArrayList {
public static void main(String[] args) {
// 1.创建ArrayList集合对象,限制集合中元素的类型为String类型
ArrayList<String> list = new ArrayList<>();
// 2.往集合对象中存储3个字符串元素 使用add()方法往集合中添加元素
list.add("唐三");
list.add("小舞");
list.add("荣荣");
// 3.获取集合中元素的个数
//int size = list.size();
// 4.循环遍历集合中每一个元素
for (int i = 0;i<list.size();i++){
// 获取元素,并打印
System.out.println(list.get(i));
}
}
}
ArrayList存储学生对象并遍历
需求
- 创建一个存储学生对象的集合,存储3个学生对象,使用程序实现在控制台遍历该集合
分析
- 定义学生类
- 创建集合对象
- 创建学生对象
- 添加学生对象到集合中
- 遍历集合,采用通用遍历格式实现
实现
import java.util.ArrayList;
/*
ArrayList存储学生对象并遍历
需求:
- 创建一个存储学生对象的集合,存储3个学生对象,使用程序实现在控制台遍历该集合
分析:
1.创建学生类
2.创建3个学生对象
3.创建ArrayList集合对象,限制集合中元素的类型为学生类类型
4.往集合中存储那3个学生对象
5.循环遍历集合中的元素,打印输出
*/
public class Test2ArrayList {
public static void main(String[] args) {
// 创建3个学生对象
Student stu1 = new Student("萧炎",18);
Student stu2 = new Student("薰儿",19);
Student stu3 = new Student("美杜莎",20);
// 创建ArrayList集合对象,限制集合中元素的类型为学生类类型
ArrayList<Student> list = new ArrayList<>();
// 往集合中存储那3个学生对象
list.add(stu1);
list.add(stu2);
list.add(stu3);
// 循环遍历集合中的元素,打印输出
for (int i = 0;i<list.size();i++){
// 获取集合元素
Student stu = list.get(i);
// System.out.println("stu:"+stu);// 打印地址值
System.out.println( stu.getName()+","+ stu.getAge());
}
}
}
ArrayList存储学生对象并遍历升级版
需求
- 创建一个存储学生对象的集合,存储3个学生对象,使用程序实现在控制台遍历该集合 ( 学生的姓名和年龄来自于键盘录入)
分析
- 定义学生类,为了键盘录入数据方便,把学生类中的成员变量都定义为String类型
- 创建集合对象
- 键盘录入学生对象所需要的数据
- 创建学生对象,把键盘录入的数据赋值给学生对象的成员变量
- 往集合中添加学生对象
- 遍历集合,采用通用遍历格式实现
实现
/*
思路:
1:定义学生类,为了键盘录入数据方便,把学生类中的成员变量都定义为String类型
2:创建集合对象
3:键盘录入学生对象所需要的数据
4:创建学生对象,把键盘录入的数据赋值给学生对象的成员变量
5:往集合中添加学生对象
6:遍历集合,采用通用遍历格式实现
*/
public class ArrayListTest {
public static void main(String[] args) {
//创建集合对象
ArrayList<Student> array = new ArrayList<Student>();
//为了提高代码的复用性,我们用方法来改进程序
addStudent(array);
addStudent(array);
addStudent(array);
//遍历集合,采用通用遍历格式实现
for (int i = 0; i < array.size(); i++) {
Student s = array.get(i);
System.out.println(s.getName() + "," + s.getAge());
}
}
/*
两个明确:
返回值类型:void
参数:ArrayList<Student> array
*/
public static void addStudent(ArrayList<Student> array) {
//键盘录入学生对象所需要的数据
Scanner sc = new Scanner(System.in);
System.out.println("请输入学生姓名:");
String name = sc.nextLine();
System.out.println("请输入学生年龄:");
String age = sc.nextLine();
//创建学生对象,把键盘录入的数据赋值给学生对象的成员变量
Student s = new Student();
s.setName(name);
s.setAge(age);
//往集合中添加学生对象
array.add(s);
}
}
集合综合案例
案例介绍
按照斗地主的规则,完成洗牌发牌的动作。
具体规则:
使用54张牌打乱顺序,三个玩家参与游戏,三人交替摸牌,每人17张牌,最后三张留作底牌。
####案例分析
-
准备牌:
牌可以设计为一个ArrayList,每个字符串为一张牌。
每张牌由花色数字两部分组成,我们可以使用花色集合与数字集合嵌套迭代完成每张牌的组装。
牌由Collections类的shuffle方法进行随机排序。 -
发牌
将每个人以及底牌设计为ArrayList,将最后3张牌直接存放于底牌,剩余牌通过对3取模依次发牌。
-
看牌
直接打印每个集合。
6.3 代码实现
import java.util.ArrayList;
import java.util.Collections;
public class Poker {
public static void main(String[] args) {
/*
* 1: 准备牌操作
*/
//1.1 创建牌盒 将来存储牌面的
ArrayList<String> pokerBox = new ArrayList<String>();
//1.2 创建花色集合
ArrayList<String> colors = new ArrayList<String>();
//1.3 创建数字集合
ArrayList<String> numbers = new ArrayList<String>();
//1.4 分别给花色 以及 数字集合添加元素
colors.add("♥");
colors.add("♦");
colors.add("♠");
colors.add("♣");
for(int i = 2;i<=10;i++){
numbers.add(i+"");
}
numbers.add("J");
numbers.add("Q");
numbers.add("K");
numbers.add("A");
//1.5 创造牌 拼接牌操作
// 拿出每一个花色 然后跟每一个数字 进行结合 存储到牌盒中
for (String color : colors) {
//color每一个花色 guilian
//遍历数字集合
for(String number : numbers){
//结合
String card = color+number;
//存储到牌盒中
pokerBox.add(card);
}
}
//1.6大王小王
pokerBox.add("小☺");
pokerBox.add("大☠");
// System.out.println(pokerBox);
//洗牌 是不是就是将 牌盒中 牌的索引打乱
// Collections类 工具类 都是 静态方法
// shuffer方法
/*
* static void shuffle(List<?> list)
* 使用默认随机源对指定列表进行置换。
*/
//2:洗牌
Collections.shuffle(pokerBox);
//3 发牌
//3.1 创建 三个 玩家集合 创建一个底牌集合
ArrayList<String> player1 = new ArrayList<String>();
ArrayList<String> player2 = new ArrayList<String>();
ArrayList<String> player3 = new ArrayList<String>();
ArrayList<String> dipai = new ArrayList<String>();
//遍历 牌盒 必须知道索引
for(int i = 0;i<pokerBox.size();i++){
//获取 牌面
String card = pokerBox.get(i);
//留出三张底牌 存到 底牌集合中
if(i>=51){//存到底牌集合中
dipai.add(card);
} else {
//玩家1 %3 ==0
if(i%3==0){
player1.add(card);
}else if(i%3==1){//玩家2
player2.add(card);
}else{//玩家3
player3.add(card);
}
}
}
//看看
System.out.println("令狐冲:"+player1);
System.out.println("田伯光:"+player2);
System.out.println("绿竹翁:"+player3);
System.out.println("底牌:"+dipai);
}
}
一. Set接口
List : 有序, 有索引, 可以重复
Set :
无序 : 存和取的顺序不一致
没有索引
不可以重复(保证元素的唯一)
Set集合的学习重点, 放在学习如何保证元素的唯一
1. HashSet
- 存储Java已经提供好的数据类型
- 可以保证元素的唯一
HashSet<String> set = new HashSet<>();
set.add("潮汐海灵");
set.add("潮汐海灵");
set.add("丽桑卓");
set.add("丽桑卓");
set.add("劫");
set.add("劫");
System.out.println(set); //[潮汐海灵, 劫, 丽桑卓]
2. HashSet存储自定义类型(根据hashCode()返回值判断是否存入)
如果存入元素的hashCode()返回值是不同, 元素会直接存入集合
如何存入元素的hashCode()和集合中已有的hashCode()相同, 会调用equals()进行比较
public class Demo03 {
public static void main(String[] args) {
// 存放Hero类型的Set集合
HashSet<Hero> set = new HashSet<>();
// 添加元素
set.add(new Hero("潮汐海灵", "大鲨鱼"));
set.add(new Hero("潮汐海灵", "大鲨鱼"));
set.add(new Hero("亚索", "哈萨key"));
set.add(new Hero("亚索", "哈萨key"));
set.add(new Hero("伊泽瑞尔", "精准弹幕"));
set.add(new Hero("伊泽瑞尔", "精准弹幕"));
// 遍历打印
for (Hero hero : set) {
System.out.println(hero); // 去重之后的结果
}
}
}
// =====================================================================
public class Hero {
private String name;
// 技能
private String skill;
public Hero() {
}
public Hero(String name, String skill) {
this.name = name;
this.skill = skill;
}
// get/set省略
/*
重写之后比较的是属性值
*/
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Hero hero = (Hero) o;
return Objects.equals(name, hero.name) &&
Objects.equals(skill, hero.skill);
}
/*
使用属性, 进行一系列的计算(为了属性不同, 让hashCode()一定不同), 最终得到一个值
*/
@Override
public int hashCode() {
return Objects.hash(name, skill);
}
@Override
public String toString() {
return "Hero{" +
"name='" + name + '\'' +
", skill='" + skill + '\'' +
'}';
}
}
总结: 如果使用HashSet集合存储自定义对象, 想保证元素的唯一, 需要重写hashCode()和equals()方法
3. LinkedHashSet
LinkedHashSet是HashSet集合的子类
Set集合中惟一一个有序的集合
特点
- 可以保证元素的唯一
- 由于是链表, 可以保证有序(怎么存就怎么取)
4. TreeSet
TreeSet集合能够排序, 是因为存入集合的元素自身具备比较功能
特点
- 可以保证元素的唯一
- 可以对元素进行从小到大的排序(自然排序)
TreeSet中的二叉树
Collections
用来操作集合的工具类
1. 可变参数
ackage com.free.collections;
/*
可变参数
格式: 数据类型 ... 变量名
特点: 如果方法的形参是可变参数, 调用方法时可以传入对应数据类型的任意个参数
可变参数, 其实就是一个数组
注意事项: 形参中如果有可变参数, 将可变参数放到参数列表的最后
*/
public class Demo01 {
public static void main(String[] args) {
System.out.println(getSum());
System.out.println(getSum(1));
System.out.println(getSum(1, 2));
System.out.println(getSum(1,2,3));
System.out.println(getSum(1,2,3,4,5,6));
System.out.println(getSum(1,2,3,4,5,6,7,8,9,10));
}
public static int getSum(int... a) { // a : 数组
int sum = 0;
// 遍历
for (int i : a) {
sum += i;
}
return sum;
}
// 问题, 方法的参数列表中, 可能会传入同一种数据类型的任意个参数
/*public static int getSum(int a, int b) {
return a + b;
}
public static int getSum(int a, int b , int c) {
return a + b + c;
}*/
// ...
}
2. 常用功能
- addAll
public static <T> boolean addAll(Collection<T> c, T... elements)
将elements可变参数中的所有内容, 添加到c集合中
由于形参定义的是Collection集合, 随意这个方法可以添加所有Colleciton的子类(ArrayList, HashSet ...)
- shuffle
public static void shuffle(List<?> list) : 随机置换
注意: 只能操作List集合, 不能操作Set集合
- sort
public static <T extends Comparable<T>> void sort(List<T> list) :
排序
集合泛型的数据类型, 必须是Comparable的实现类
Comparable: 自然排序 -> 从小到大的升序排序
3. 比较器
public static <T> void sort(List<T> list, Comparator<T> c)
(1) 集合元素是Integer类型的排序
ArrayList<Integer> list = new ArrayList<>();
// 添加元素
list.add(70);
list.add(50);
list.add(20);
list.add(50);
list.add(40);
// 匿名内部类: new 接口名() {} -> 实现了该接口的实现类对象
Collections.sort(list, new Comparator<Integer>() {
/*
返回值的作用
1 - 2 -> 升序
2 - 1 -> 降序
*/
@Override
public int compare(Integer o1, Integer o2) {
return o2 - o1;
}
});
System.out.println(list);
[70, 50, 50, 40, 20]
(2) 集合元素是String类型的排序
package com.itheima._03comparator;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
/*
String字符串中比较器排序的使用
*/
public class Demo02 {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
Collections.addAll(list, "aaa", "b", "cc");
// 自然排序: 首字母升序
// Collections.sort(list);
// 首字母降序
Collections.sort(list, new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
// 字符串对象.compareTo(另一个字符串对象)
// return o1.compareTo(o2); // 字典顺序升序
// return o2.compareTo(o1); // 字典顺序降序
// return o1.length() - o2.length(); // 长度升序
return o2.length() - o1.length(); // 长度降序
}
});
System.out.println(list);
}
}
(3) 集合元素是自定义类型的简单排序
package com.itheima._03comparator;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
public class Demo03 {
public static void main(String[] args) {
ArrayList<Hero> list = new ArrayList<>();
list.add(new Hero("a奈文摩尔", 1));
list.add(new Hero("b雷克萨", 3));
list.add(new Hero("b雷克萨", 4));
list.add(new Hero("c奥蕾莉亚", 2));
// 如果两种排序功能都存在, 使用比较器排序
// 不报错: Integer, String
Collections.sort(list, new Comparator<Hero>() {
@Override
public int compare(Hero o1, Hero o2) {
return o2.getName().compareTo(o1.getName());
}
});
System.out.println(list);
}
}
(4) 集合元素是自定义类型的进阶排序
package com.itheima._03comparator;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
/*
先按照姓名降序, 如果姓名相同, 按照排名升序
*/
public class Demo04 {
public static void main(String[] args) {
ArrayList<Hero> list = new ArrayList<>();
list.add(new Hero("a奈文摩尔", 1));
list.add(new Hero("b雷克萨", 4));
list.add(new Hero("b雷克萨", 2));
list.add(new Hero("b雷克萨", 3));
list.add(new Hero("b雷克萨", 1));
list.add(new Hero("c奥蕾莉亚", 2));
Collections.sort(list, new Comparator<Hero>() {
@Override
public int compare(Hero o1, Hero o2) {
// 如果姓名相同, 比较rank
// o2.getName().compareTo(o1.getName() 返回 0, 说明姓名相同
int num = o2.getName().compareTo(o1.getName());
/*if (num == 0) {
return o1.getRank() - o2.getRank();
} else { // 姓名不同
return num;
}*/
return num == 0 ? o1.getRank() - o2.getRank() : num;
}
});
System.out.println(list);
}
}
(5) TreeSet也可以使用比较器
public TreeSet(Comparator<? super E> comparator)
有Studnet类 [name:String , age:int, math:int, english:int, chinese:int, totalScore:int]
- 先按照总成绩降序排序
- 总成绩相同, 按照语文降序
- 总成绩和语文都相同, 按照数学降序
- 总成绩, 语文, 数学相同, 按照年龄降序
Map集合
1. Map集合的特点
- 将键映射到值的对象, 双列集合, 以键值对的形式存在
- Map集合中的键是唯一的
- 一个键最多对应一个值(可能没有值(null), 一个值)
键值对形式存在
键是唯一的
2. 双列集合的继承体系
3. Map中的常用方法
Map<K,V> K : key 键 V : value 值
(1) 增删改查
- 增加/修改
V put(K key, V value) : 向集合中添加一组键值对,
如果键相同, 值覆盖
返回值: 返回的是替换掉的值
- 删除
V remove(Object key) : 删除指定键对应的, 键值对
返回值: 返回被删除掉的值
- 查询
V get(Object key) : 根据键, 获取值
(2) 其他操作
- 长度
int size() : 获取集合中, 键值对 的个数
- 判断是否包含
boolean containsKey(Object key) : 判断集合中是否包含指定键
boolean containsValue(Object value) : 判断集合中是否包含指定值
- 获取所有键
Set<K> keySet() : 获取Map集合中所有键存在的Set集合
返回值: 返回存放所有键的Set集合
- 获取所有值
Collection<V> values() : 获取Map集合中所有值存在的Colletion集合
4. Map集合的遍历
(1) 键找值
- 获取所有的键
- 通过键获取值
HashMap<String, String> map = new HashMap<>();
map.put("C罗", "葡萄牙");
map.put("梅西", "阿根廷");
map.put("德布劳内", "比利时");
map.put("内马尔", "巴西");
map.put("武磊", "中国");
// 获取所有键 -> Set<K>
Set<String> keySet = map.keySet();
// 遍历Set集合, 获取每一个键
for (String key : keySet) {
// 根据键获取值
String value = map.get(key);
System.out.println(key + "=" + value);
}
(2) 键值对
Set<Map.Entry<K,V>> entrySet() : 获取所有键值对 所在的 Set集合
Set<键值对>
HashMap<String, String> map = new HashMap<>();
map.put("C罗", "葡萄牙");
map.put("梅西", "阿根廷");
map.put("德布劳内", "比利时");
map.put("内马尔", "巴西");
map.put("武磊", "中国");
// Set集合存放String -> Set<String>
// Set集合中存放的是 键值对
Set<Map.Entry<String, String>> entrySet = map.entrySet();
// 遍历Set集合, 获取每一个键值对
// Map.Entry<String, String> -> 键值对
for (Map.Entry<String, String> entry : entrySet) {
// 键值对类型中有两个功能: getKey(), getValue()
String key = entry.getKey();
String value = entry.getValue();
System.out.println(key + "=" + value);
}
5. HashMap存储自定义类型
如果HashMap存储自定义对象作为键, 想保证键的唯一, 需要重写hashCode()和equals()
6. LinkedHashMap
特点
- 保证键的唯一
- 有序(怎么存就怎么取)
7. TreeMap
特点
- 保证键的唯一
- 可以根据键进行排序
8. 集合的嵌套
- ArrayList集合嵌套HashMap
package com.free._04map;
import java.util.ArrayList;
import java.util.HashMap;
public class Demo07 {
public static void main(String[] args) {
// 创建ArrayList集合, 集合中存放HashMap
ArrayList<HashMap<String , String>> list = new ArrayList<>();
// 创建HashMap
HashMap<String, String> map1 = new HashMap<>();
map1.put("姓名", "张三");
map1.put("年龄", "23");
map1.put("性别", "男");
HashMap<String, String> map2 = new HashMap<>();
map2.put("姓名", "李四");
map2.put("年龄", "24");
map2.put("性别", "女");
HashMap<String, String> map3 = new HashMap<>();
map3.put("姓名", "王五");
map3.put("年龄", "25");
map3.put("性别", "男");
// 将Map集合添加到List中
list.add(map1);
list.add(map2);
list.add(map3);
// 遍历List集合, 获取到每一个元素, 元素是HashMap类型
for (HashMap<String, String> map : list) {
System.out.println("~~~~~~~~~~~~~");
// 遍历HashMap
for (String key : map.keySet()) {
System.out.println(key + "=" + map.get(key));
}
}
}
}
9. Map集合的练习
- 需求: 键盘录入一个字符串, 获取字符串中每一个字符出现的次数
- 键盘录入: abcdabcdab -> a(3)b(3)c(2)d(2)
package com.free._01test;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;
/*
键盘录入字符串, 获取字符串中每个字符出现的次数
"abcdabc" -> a(2)b(2)c(2)d(1)
*/
public class Test01 {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
// 获取键盘录入的字符串
System.out.println("请输入字符串:");
String s = sc.nextLine();
// 创建Map集合
HashMap<Character, Integer> map = new HashMap<>();
// 遍历字符串, 获取到每一个字符
//toCharArray() 将此字符串转换为新的字符数组。
for (char c : s.toCharArray()) {
// 如果字符不存在, 值为1
/*if (!map.containsKey(c)) {
// 如果这个字符不存在, 那一定是第一次获取到这个字符串, 值写成1
map.put(c, 1);
} else {
// 获取到之前的值
int times = map.get(c);
// 集合中已经存在这个键
map.put(c, times + 1);
}*/
// 三元运算符
map.put(c, !map.containsKey(c) ? 1 : map.get(c) + 1);
}
// 创建字符串缓冲区(StringBuilder)
StringBuilder sb = new StringBuilder();
// 遍历map集合
for (Map.Entry<Character, Integer> entry : map.entrySet()) {
sb.append(entry.getKey()).append("(").append(entry.getValue()).append(")");
}
System.out.println(sb);
}
}
四. HashMap集合的源码解析
- jdk7以及以前 : 数组 + 链表
- jdk8开始: 数组 + 链表 + 红黑树
public class HashMap<K,V> {
/**
* The default initial capacity - MUST be a power of two.
* 默认初始容量 : 16
* 1 << n : 1 * 2的n次方
*/
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
// 最大容量
static final int MAXIMUM_CAPACITY = 1 << 30;
/**
* The load factor used when none specified in constructor.
* 负载因子(加载因子) : 0.75
* 当占用的容量, 超过当前最大容量的0.75倍, 就扩容
*
* 16 * 0.75 = 12 -> 超过12就扩容
*/
static final float DEFAULT_LOAD_FACTOR = 0.75f;
/*
* 链表的长度大于等于8的时候, 将链表转换成红黑树
* 还有一个条件: 容器中元素的个数大于等于64
*/
static final int TREEIFY_THRESHOLD = 8;
static final int MIN_TREEIFY_CAPACITY = 64;
/*
* 如果红黑树的长度小于6, 会退化成链表
*/
static final int UNTREEIFY_THRESHOLD = 6;
}
位移算法。 例如:4<<2
4的二进制是:0000 0100
<<表示往左移两位:00 010000
只要把4转换成二进制,往左移两位,再转换成10进制得出结果既是:16
更简单的计算方法就是 4<< n 等效于 4 乘以 2的 N 次方
斗地主排序
package com.free._01test;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.TreeSet;
/*
斗地主排序
*/
public class Test03 {
public static void main(String[] args) {
// 创建HashMap集合, 键是编号(从小到大, 牌的大小顺序), 值是牌
HashMap<Integer, String> map = new HashMap<>();
// 创建ArrayList集合, 存放的就是Map集合中的键
ArrayList<Integer> list = new ArrayList<>();
// 发牌
// 定义一个int类型的变量, 当做牌的大小顺序(编号)
int count = 1;
// 创建用来存放点数的容器 - 数组
String[] numbers = {"3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", "A", "2"};
// 创建用来存放花色的容器
String[] colors = {"♦", "♣", "♥", "♠"};
// 拼接牌
// 先遍历点数
for (String number : numbers) {
// 再遍历花色
for (String color : colors) {
String poker = color + number;
// 存入Map集合
map.put(count, poker);
// count存入ArrayList集合
list.add(count);
count++;
}
}
// 添加大小王
map.put(count, "♖");
list.add(count++);
map.put(count, "♕");
list.add(count);
// 洗牌
Collections.shuffle(list);
// 发牌
TreeSet<Integer> me = new TreeSet<>();
TreeSet<Integer> gaoJin = new TreeSet<>();
TreeSet<Integer> zhouXingXing = new TreeSet<>();
TreeSet<Integer> left = new TreeSet<>();
for (int i = 0; i < list.size(); i++) {
if (i >= list.size() - 3) {
// 注意: 添加的是i索引位置的元素
left.add(list.get(i));
} else if (i % 3 == 0) {
me.add(list.get(i));
} else if (i % 3 == 1) {
gaoJin.add(list.get(i));
} else {
zhouXingXing.add(list.get(i));
}
}
// 看牌
lookPoker(map, left, "底牌:");
lookPoker(map, me, "我:");
lookPoker(map, gaoJin, "高进:");
lookPoker(map, zhouXingXing, "周星星:");
}
public static void lookPoker(HashMap<Integer, String> map, TreeSet<Integer> set, String name) {
StringBuilder sb = new StringBuilder(name);
// 遍历Set集合
for (Integer key : set) {
String poker = map.get(key);
sb.append(poker).append(" ");
}
System.out.println(sb);
}
}