数据结构
数据结构是计算机存储,组织数据的方式.是指相互之间存在一种或多种特定关系的数据元素的集合
通常情况下,精心选择的数据结构可以带来更高的运行或者存储效率
栈:先进后出
队列:先进先出
数组:查询快,增删慢 是一块连续的内存空间
链表:增删快,查询慢 是不连续的内存空间
树结构:
树结构特点
每一个节点有零个或者多个子节点
没有父节点的节点称之为根节点,一个树最多有一个根节点
每一个非根节点有且只有一个父节点
名词解释
节点:指树中的一个元素
节点的度:节点拥有的子树的个数,二叉树的度不大于2
叶子结点:度为0的节点,也称为终端节点
高度:叶子结点的高度为1,叶子结点的父节点高度为2,以此类推,根节点的高度最高
层:根节点在第一层,以此类推
父节点:若一个节点含有子节点,则这个节点称之为其子节点的父节点
子节点:子节点是父节点的下一层节点
兄弟节点:拥有共同父节点的节点互称为兄弟节点
平衡二叉树
结构特地啊
左右两个子树的高度差绝对值不超过1,并且左右两个子树都是一颗平衡二叉树
避免出现"瘸子"现象,减少树的高度,提高了搜索效率
红黑树
红黑树是一种自平衡的二叉查找树,是计算机科学中用到的一种数据结构
结构特性
根节点是黑色,其他节点是红色或黑色
每个红色节点的两个子节点都是黑色
如果一个节点没有儿子,我们称之为叶子结点,红黑树中,叶子被假定为null或空,每个叶子结点(Nil)是黑色的
从任一节点到其每个叶子的所有路径都包含相同数目的黑色节点
结构特点:趋近于平衡树,查询速度块
平衡原理:
红黑树的每一个节点上都有存储位表示接地那的颜色,可以是红或者黑
添加元素原理:每一次插入完毕以后,使用红黑色规则进行校验,如果不满足红黑规则,就需要左旋和右旋来调整树,使其满足红黑规则
泛型
是JDK5中引入的特性,他提供了编译时类型安全检测机制,该机制允许在编译时检测到非法的类型
他的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数
一提到参数,最熟悉的就是定义方法时有形参,然后调用此方法时传递实参.name参数化类型怎么理解呢?顾名思义,就是将类型由原来的具体的类型参数化,然后再使用/调用时传入具体的类型
这种参数类型可以用在类,方法,或接口中,分别称为泛型类,泛型方法,泛型接口
泛型定义格式:
<类型>:指定一种类型的格式,这里的类型可以看成是形参
<类型1,类型2...>:指定多种类型的格式,多种类型之间用逗号隔开,这里的类型可以看成是形参
将来具体调用的时候给定的类型可以看成是实参,并且实参的类型只能是引用数据类型
泛型的好处:
把运行时期的问题提前到了编译期间
避免了强制类型转换
ArrayList objects = new ArrayList<>(); objects.add("11"); objects.add(11); for (int i = 0; i < objects.size(); i++) { String s = (String) objects.get(i); // 该处会报类型转换异常 } ArrayList<String> strings = new ArrayList<>(); strings.add("11"); strings.add(11); // 添加了泛型后再编译期就会报错 for (String string : strings) { System.out.println(string); }
泛型类
// 泛型类的定义格式
// 格式:修饰符 class 类名<类型>{}
// 此处T可以随便写任意标识,常见的如T,E,K,V等形式的参数常用语表示泛型
public class Generic <T>{
// 在泛型类中定义泛型的数据类型变量,可以在创建对象的时候再进行指定需要的数据类型
private T t;
public Generic() {
}
public Generic(T t) {
this.t = t;
}
public T getT() {
return t;
}
public void setT(T t) {
this.t = t;
}
}
Generic<String> stringGeneric = new Generic<>();
stringGeneric.setT("string");
泛型方法
// 泛型方法的定义格式
// 格式: 修饰符<类型> 返回值类型 方法名(类型 变量名) { }
public class Generic {
public <T> void show(T t) {
System.out.println(t);
}
}
// 测试
Generic generic = new Generic();
generic.show("show");
generic.show(11);
泛型接口
// 泛型接口的定义格式
// 格式: 修饰符 interface 接口名<类型> {}
public interface InterfaceTest<T> {
void show(T t);
}
// 实现类
public class InterfaceTestImpl<T> implements InterfaceTest<T> {
@Override
public void show(T t) {
System.out.println(t);
}
}
// 测试
InterfaceTest<String> interfaceTest = new InterfaceTestImpl();
interfaceTest.show("sss");
类型通配符
为了表示各种泛型List的父类,可以使用类型通配符
类型通配符:<?>
List<?>:表示元素类型未知的List,他的元素可以匹配任何的类型
这种带通配符的List仅表示他是各种泛型List的父类,并不能把元素添加到其中
如果说我们不希望List<?>是任何泛型List的父类,只希望他代表某一泛型List的父类或子类,可以使用类型通配符的上限或下限
类型通配符上限:<? extends 类型>
List<? extends Number>: 他表示类型是Number或者其子类型
类型通配符下限:<? super 类型>
List<? super Number>: 他表示类型是Number或者其父类型
ArrayList<? extends List> list = new ArrayList<ArrayList>(); ArrayList<? extends List> list2 = new ArrayList<List>(); ArrayList<? super List> list3 = new ArrayList<List>(); ArrayList<? super List> list4 = new ArrayList<Collection>();
可变参数
可变参数又称参数个数可变,用作方法的形参出现,name方法参数个数就是可变的了
// 格式: 修饰符 返回值类型 方法名(数据类型...变量名){} public void show(String... s){ // 可变参数会被封装到一个数组中 System.out.println(s.length); } // 可变参数的注意事项 // 如果一个方法有多个参数,包含可变参数,可变参数要放在最后 // 一个方法只可以有一个可变参数
可变参数的使用
类 方法 说明 Arrays工具类中的静态方法 public static<T>List<T>asList(T...a) 返回由指定数组支持的固定大小的列表 List接口中的静态方法JDK9新特性 public static<E>List<E>of(E...elements) 返回包含任意数量元素的不可变列表 Set接口中的静态方法JDK9新特性 public static<E>Set<E>of(E...elements) 返回一个包含任意数量元素的不可变集合 Collections工具类中的静态方法 public static <T> boolean addAll(Collection<T> c,T... elements) 将可变参数中所有的内容,都添加到指定集合中 // asList方法返回的集合不可以做增删操作,可以做修改操作 List<String> list1 = Arrays.asList("11", "22"); // list1.add("33"); // UnsupportedOperationException // list1.remove(1); // UnsupportedOperationException list1.set(1,"44"); // List接口中的of方法时JDK9的新特性,返回的集合不可以做增删改操作 List<String> list5 = List.of("11", "22", "33"); // list5.add("44"); // UnsupportedOperationException // list5.remove("11"); // UnsupportedOperationException // list5.set(1,"88"); // UnsupportedOperationException // Set接口中的of方法是JDK9的新特性,返回的集合不能做增删操作,没有修改的方法,在给元素的时候,不能给重复的元素 Set<String> set1 = Set.of("11", "33", "55"); // Set<String> set2 = Set.of("11", "33", "55","22"); // UnsupportedOperationException // set1.add("11");// UnsupportedOperationException // set1.remove("11");// UnsupportedOperationException }
集合
-
特点:提供一种存储空间可变的存储模型,存储的数据容量可以发生改变
集合类的体系结构
单列集合Collection接口
List接口:可重复
ArrayList实现类
LinkedList实现类
Set接口:不可重复
HashSet实现类
TreeSet实现类
双列集合Map接口
HashMap实现类
Collection
Collection集合概述
是单列集合的顶层接口,他表示一组对象,这些对象也称为Collection的元素
JDK不提供此接口的任何直接实现,他提供更具体的子接口实现(如Set和List)
Collection集合常用方法
方法名 | 说明 |
---|---|
boolean add(E e) | 添加元素 |
boolean remove(Object o) | 从集合中移除指定的元素 |
void clear() | 清空集合中的元素 |
boolean contains(Object o) | 判断集合中是否存在指定的元素 |
boolean isEmpty() | 判断集合是否为空 |
int size() | 集合的长度,也就是集合中元素的个数 |
Iterator<E> iterator() | 返回此集合的迭代器 |
Collection集合的遍历
Iterator:迭代器,集合的专用遍历方式
Iterator<E> iteratror():返回此集合中元素的迭代器,通过集合的iterator()方法得到
迭代器是通过集合的iterator()方法得到的,所以我们说他是依赖于集合而存在的
Iterator中的常用方法
方法 说明 E next() 返回迭代中的下一个元素 boolean hasNext() 如果迭代具有更多元素,则返回true default void remove() 使用迭代器删除集合中的元素
// 创建集合,并往集合中添加元素
Collection<String> collection = new ArrayList<String>();
collection.add("hello");
collection.add("world");
collection.add("java");
// 获取集合的迭代器
Iterator<String> iterator = collection.iterator();
/* System.out.println(iterator.next());
System.out.println(iterator.next());
System.out.println(iterator.next());
System.out.println(iterator.next()); //NoSuchElementException*/
// 判断集合中是否还有元素,如果有的话就进行输出
while (iterator.hasNext()) {
String next = iterator.next();
System.out.println(next);
}
并发修改异常
使用集合获取迭代器后,使用迭代器的next()方法会去判断集合的可修改次数是否发生了改变,如果发生了改变就会报错
-
ConcurrentModificationException
-
产生原因
-
迭代器遍历的过程中,通过集合对象修改了集合中元素的长度,造成了迭代器获取元素中判断预期修改值和实际修改至不一致
-
解决方法
-
用for循环遍历,然后用集合对象做对应的操作即可
增强for循环
增强for:简化数组和collection集合的遍历
实现Iterable接口的类允许其对象称为增强型for语句的目标
他是JDK5之后出现的,其内部原理是一个Iterator迭代器
增强for的格式
for(元素数据类型 变量名:数组或者Collection集合) {
// 在此处使用变量即可,该变量就是元素
}
int[] arr = {1, 2, 3};
for (int i : arr) {
System.out.println(i);
}
Collection<String> list1 = new ArrayList<>();
list1.add("hello");
list1.add("world");
list1.add("java");
for (String s : list1) {
if (s.equals("hello")) {
list1.add("javaee"); // ConcurrentModificationException 并发修改异常
}
System.out.println(s);
}
Set
不包含重复元素的集合
HashSet集合存储元素
要保证元素唯一性,需要重写hashCode()和equals()方法
没有带索引的方法,所以不能使用普通for循环遍历
HashSet:对集合的迭代顺序不作任何保证
Set<String> set = new HashSet<>(); set.add("hello"); set.add("world"); set.add("java"); set.add("java"); for (String s : set) { System.out.println(s); //world java hello } // hashSet是通过hashCode和equals方法保证元素的唯一 // hashSet存储自定义对象,自定义对象需要重写hashCode和equals方法来保证元素的唯一
LinkedHashSet
特点
哈希表和链表实现的Set接口,具有可预测的迭代次序
由链表保证元素有序,也就是说元素的存储和取出顺序是一致的
由哈希表保证元素唯一,也就是说没有重复的元素
TreeSet
特点
元素有序,这里的顺序不是指存储和取出的顺序,而是按照一定的规则进行排序,具体排序方式取决于构造方法
没有带索引的方法,所以不能使用普通for循环遍历
由于是Set集合,所以不包含重复元素的集合
注意事项:
用TreeSet集合存储自定义对象,无参构造方法使用的是自然排序对元素进行排序的
自然排序,就是让元素所属的类实现Comparable接口,重写compareTo(To)方法
重写方法时,一定要注意排序规则必须按照要求的主要条件和次要条件来写
public class Student implements Comparable<Student> { private String name; private int age; public Student() { } 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 int hashCode() { return Objects.hash(name, age); } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public String toString() { return "Student{" + "name='" + name + '\'' + ", age=" + age + '}'; } @Override public int compareTo(Student o) { //按照年龄从小到大顺序排序,年龄相同时根据姓名的字母顺序进行排序 // 返回值是正数的话就是从小到大排序,负数就是从大到小,返回值为0则不会向TreeSet中添加该元素 int num = this.age - o.age; return num == 0 ? this.name.compareTo(o.name) : num; } } // 测试类 Set<Student> set = new TreeSet<>(); set.add(new Student("2l",22)); set.add(new Student("1mz",22)); set.add(new Student("mlz",24)); set.add(new Student("zml",25)); for (Student s: set) { System.out.println(s); } // 如果自定义引用数据类型没有实现Comparator接口则可以使用带参构造方法来创建TreeSet集合 TreeSet<Student> students = new TreeSet<>(new Comparator<Student>() { @Override public int compare(Student o1, Student o2) { int num = o1.getAge() - o2.getAge(); return num == 0 ? o1.getName().compareTo(o2.getName()) : num; } }); students.add(new Student("test",22)); students.add(new Student("test",21)); students.add(new Student("test",23)); Iterator<Student> iterator = students.iterator(); while (iterator.hasNext()) { Student next = iterator.next(); System.out.println(next); }
构造方法 说明 TreeSet() 根据其元素的自然顺序进行排序 TreeSet(Comparator comparator) 根据指定的比较器进行排序
哈希值
是JDK根据对象的地址或者字符串或者数字算出来的int类型的数值
Object类中有一个方法可以获取对象的哈希值
pubic int hashCode():返回对象的哈希码值
对象的哈希值特点
同一个对象多次的调用hashCode()方法返回的哈希值是相同的
默认情况下,不同对象的哈希值是不同的,而重写hashCode()方法.可以实现让不同对象的哈希值相同
哈希表
JDK8之前,底层采用数组+链表实现可以说是一个元素为链表的数组
JDK8以后,在长度比较长的时候,底层实现了优化
默认初始长度是16
List
有序集合(也称为序列),用户可以精确控制列表中每个元素的插入位置.用户可以通过整数索引访问元素,并搜索列表中的元素
与Set集合不同,列表通常允许重复的元素
特点:
有序:存储和取出的元素顺序一致
可重复:存储的元素可以重复
List集合常用子类:ArrayList,LinkedList
ArrayList:底层数据结构是数组,查询快,增删慢
LinkedList:底层数据结构是链表,查询慢,增删快
List集合中的特有方法:
方法名 | 说明 |
---|---|
void add(int index,E element) | 在此集合中的指定位置插入指定的元素 |
E remove(int index) | 删除指定索引处的元素,返回被删除的元素 |
E set(int index,E element) | 修改指定索引处的元素,返回被修改的元素 |
E get(int index) | 返回制定索引处的元素 |
int size() | 返回集合中元素的个数 |
ListIterator listIterator() | 返回list集合中的特有迭代器 |
public static <E>List<E>of(E...elements) | 返回包含任意数量元素的不可变列表 |
ListIterator
列表迭代器
通过List集合的listIterator()方法得到,所以说他是List集合特有的迭代器
用于允许程序员沿任一方向遍历列表的列表迭代器,在迭代期间修改列表,并获取列表中迭代器的当前位置
ListIterator()中的常用方法
方法名 说明 E next() 返回迭代中的下一个元素 E previous() 返回列表中的上一个元素 boolean hasNext() 如果迭代具有更多元素则返回true boolean hasPrevious() 如果此列表迭代器在相反方向遍历列表时具有更多元素,则返回true void add(E e) 将指定的元素插入列表 ListIterator中的add方法不会出现并发修改异常,因为在add方法中会将实际修改值赋值给预期修改值
LinkedList
LinkedList集合的特有功能
方法名 说明 public void addFirst(E e) 在该列表开头插入指定的元素 public void addLast(E e) 将指定的元素追加到此列表的末尾 public E getFirst() 返回此列表中的第一个元素 public E getLast() 返回此列表中的最后一个元素 public E removeFirst() 从此列表中删除并返回第一个元素 public E removeLast() 从此列表中删除并返回最后一个元素 public E pop() 从此列表所表示的堆栈处弹出一个元素 public void push(E e) 将元素推入此列表所表示的堆栈
ArrayList
<!--Class ArrayList<E> 可以调整大小的数组实现 <E>是一种特殊的数据类型,泛型-->
构造方法
方法名 | 说明 |
---|---|
public ArrayList() | 创建一个空的集合对象 |
常用方法
方法名 | 说明 |
---|---|
public boolean add(E e) | 将指定元素追加到此集合的末尾 |
public void add(int index, E element) | 在此集合中的指定位置插入指定的元素 |
public int size() | 获取集合的长度 |
public E get(int index) | 获取集合中索引处的元素 |
public boolean remove(Object obj) | 删除指定的元素,返回删除是否成功 |
public E remove(int index) | 删除指定索引处的元素,返回被删除的元素 |
public E set(int index, E element) | 修改指定索引处的元素,返回被修改的元素 |
ArrayList<String> list = new ArrayList<String>(); // 后面的泛型可以省略,是JDK7以后的新特性
boolean hello = list.add("hello");
list.add("word");
list.add("java");
// list.add(5,"javase"); IndexOutOfBoundsException
学生管理系统案例
/**
* 学生类
* @author zml
* @date 2021年11月07日18:20:30
*/
public class Student {
/**
* 学号
*/
private String sid;
/**
* 姓名
*/
private String name;
/**
* 年龄
*/
private String age;
/**
* 居住地
*/
private String address;
public Student() {
}
public Student(String sid, String name, String age, String address) {
this.sid = sid;
this.name = name;
this.age = age;
this.address = address;
}
public String getSid() {
return sid;
}
public void setSid(String sid) {
this.sid = sid;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAge() {
return age;
}
public void setAge(String age) {
this.age = age;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
}
/**
* 学生管理系统
*
* @author zml
* @date 2021年11月07日18:25:18
*/
public class StudentManager {
private static Scanner sc = new Scanner(System.in);
private static ArrayList<Student> students = new ArrayList<>();
public static void main(String[] args) {
homePage();
}
/**
* 首页
*/
public static void homePage() {
while (true) {
System.out.println("-------欢迎来到学生管理系统-------");
System.out.println("1:添加学生");
System.out.println("2:删除学生");
System.out.println("3:修改学生");
System.out.println("4:查看所有学生");
System.out.println("5: 退出");
System.out.println("请输入你的选择");
String num = sc.nextLine();
switch (num) {
case "1":
insertStudent();
break;
case "2":
deleteStudent();
break;
case "3":
updateStudent();
break;
case "4":
showStudent();
break;
case "5":
System.out.println("谢谢使用");
// System.exit(0);
return;
default:
System.out.println("你输入的信息有误,请重新输入");
}
}
}
/**
* 添加学生
*/
public static void insertStudent() {
String sid = "";
boolean flag = true;
while (flag) {
System.out.println("请输入学生学号");
sid = sc.nextLine();
flag = checkStudentsId(sid);
if (flag) {
System.out.println("您输入的学号已存在,请重新输入");
}
}
System.out.println("请输入学生姓名");
String name = sc.nextLine();
System.out.println("请输入学生年龄");
String age = sc.nextLine();
System.out.println("请输入学生居住地");
String address = sc.nextLine();
Student student = new Student(sid, name, age, address);
students.add(student);
System.out.println("添加学生成功");
}
public static void deleteStudent() {
System.out.println("请输入你要删除的学生学号");
ArrayList<String> names = new ArrayList<>();
String sidNum = sc.nextLine();
int flag = students.size();
for (int i = students.size() - 1; i >= 0; i--) {
Student student = students.get(i);
String sid = student.getSid();
if (sid.equals(sidNum)) {
Student stu = students.remove(i);
String name = stu.getName();
names.add(name);
}
}
if (students.size() < flag) {
System.out.println("删除学生成功,删除的学生有:" + names);
} else {
System.out.println("你输入的学号不存在");
}
}
public static void updateStudent() {
System.out.println("请输入你要修改的学号");
String sidNum = sc.nextLine();
for (int i = 0; i < students.size(); i++) {
Student student = students.get(i);
if (student.getSid().equals(sidNum)) {
System.out.println("请输入新学生姓名");
String name = sc.nextLine();
students.get(i).setName(name);
System.out.println("请输入新学生年龄");
String age = sc.nextLine();
students.get(i).setAge(age);
System.out.println("请输入新学生居住地");
String address = sc.nextLine();
students.get(i).setAddress(address);
// Student stu = new Student(sidNum, name, age, address);
// students.set(i, stu);
System.out.println("修改学生信息成功");
} else {
System.out.println("你输入的学号不存在");
}
}
}
public static void showStudent() {
if (students.size() == 0) {
System.out.println("无学生,请先添加");
return;
}
for (int i = 0; i < students.size(); i++) {
Student student = students.get(i);
System.out.println(student.getSid() + "\t\t" + student.getName()
+ "\t" + student.getAge() + "\t" + student.getAddress());
}
}
public static boolean checkStudentsId(String sid) {
boolean flag = false;
for (int i = 0; i < students.size(); i++) {
String sid1 = students.get(i).getSid();
if (sid.equals(sid1)) {
flag = true;
}
}
return flag;
}
public static void updateStudent() {
System.out.println("请输入你要修改的学号");
String sidNum = sc.nextLine();
for (int i = 0; i < students.size(); i++) {
Student student = students.get(i);
if (student.getSid().equals(sidNum)) {
System.out.println("请输入新学生姓名");
String name = sc.nextLine();
students.get(i).setName(name);
System.out.println("请输入新学生年龄");
String age = sc.nextLine();
students.get(i).setAge(age);
System.out.println("请输入新学生居住地");
String address = sc.nextLine();
students.get(i).setAddress(address);
// Student stu = new Student(sidNum, name, age, address);
// students.set(i, stu);
System.out.println("修改学生信息成功");
} else {
System.out.println("你输入的学号不存在");
}
}
}
}
// 此代码方法可以再进行抽取,有时间的话可以进行优化
Map
Map集合概述
Interface Map<K,V> K:键的类型;V:值得类型
将键映射到值得对象;不能包含重复的键;每个键可以映射到最多一个值
一般使用其实现类HashMap来创建对象,TreeMap是有序Map集合
集合中的键相同的话,新的值会替换掉旧值
HashMap
HashMap在JDK1.7:是由数组加链表组成
多线程情况下+reHash会产生环形链表:多线程和扩容的时候
创建HashMap集合的时候直接初始化Hash表
JDK1.8的HashMap:是由数组加链表加红黑树组成O(n)-O(logn)
扰动函数:(h=key.hashCode())^(h>>>16)
路由寻址(n-1)&hash n:数组的长度
hash冲突(碰撞):通过hash值,计算出来位置之后,两个元素的位置相同
HashMap解决hash冲突使用的拉链法(尾插)
map集合的常用方法
方法名 | 说明 |
---|---|
V put(K key,V value) | 添加元素,K重复的情况下会使用新值替换掉旧值 |
V remove(Object key) | 根据键删除键值对元素 |
void clear() | 移除所有的键值对元素 |
boolean containsKey(Object key) | 判断集合是否包含指定的键 |
boolean containsValue(Object value) | 判断集合是否包含指定的值 |
boolean isEmpty() | 判断集合是否为空 |
int size() | 集合的长度,也就是集合中键值对的个数 |
V get(Object key) | 根据键获取值 |
Set<K> keySet() | 获取所有键的集合 |
Collection<V>values() | 获取所有值的集合 |
Set<Map.Entry<K,V>> entrySet() | 获取所有键值对对象的集合 |
Map.Entry对象的方法
方法名 | 说明 |
---|---|
K getKey() | 获取键 |
V getValue() | 获取值 |
V setValue(V value) | 设置entry对象的值 |
// 创建map集合,并添加元素
Map<Integer,String> map = new HashMap<>();
map.put(1,"hello");
map.put(2,"world");
map.put(3,"java");
map.put(3,"javaee"); // map集合中的键重复的话,新值会取代旧值
System.out.println(map);
// map.remove(1); 根据键删除map中的值
// map.clear(); 清空map集合
// 获取map中所有键的集合
Set<Integer> keys = map.keySet();
// 获取map中所有值得集合
Collection<String> values = map.values();
//map集合的遍历方法1:遍历键的集合,根据键到map中获取值
for (Integer key : keys) {
System.out.println("map中的键是"+key+"map中的值是"+map.get(key));
}
// map集合的遍历方法2:获取map的键值对对象的集合,并进行遍历
Set<Map.Entry<Integer, String>> entries = map.entrySet();
for (Map.Entry<Integer, String> entry : entries) {
System.out.println(entry);
}
模拟斗地主案例
//模拟斗地主
//创建集合存储编号
ArrayList<Integer> arr = new ArrayList<>();
// 创建hashmap存储牌
HashMap<Integer, String> hm = new HashMap<>();
// 创建牌
String[] colors = {"♥", "♠", "♦", "♣"};
String[] numbers = {"3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", "A", "2"};
// 定义一个变量作为编号
int index = 1;
for (int i = 0; i < numbers.length; i++) {
for (int n = 0; n < colors.length; n++) {
arr.add(index);
hm.put(index, numbers[i] + colors[n]);
index++;
}
}
arr.add(index);
hm.put(index++, "小王");
arr.add(index);
hm.put(index, "大王");
Set<Map.Entry<Integer, String>> entries = hm.entrySet();
for (Map.Entry<Integer, String> entry : entries) {
System.out.println(entry);
}
// 使用Collections工具类进行洗牌
Collections.shuffle(arr);
// 创建三个人的牌加上底牌,为了保证编号是排序的,使用TreeSet集合
TreeSet<Integer> one = new TreeSet<>();
TreeSet<Integer> two = new TreeSet<>();
TreeSet<Integer> three = new TreeSet<>();
TreeSet<Integer> four = new TreeSet<>();
for (int i = 0; i < arr.size(); i++) {
if (i > arr.size() - 4) {
four.add(arr.get(i));
} else if (i % 3 == 0) {
one.add(arr.get(i));
} else if (i % 3 == 1) {
two.add(arr.get(i));
} else if (i % 3 == 2) {
three.add(arr.get(i));
}
}
lookPoker("zml",one,hm);
lookPoker("zm",two,hm);
lookPoker("z",three,hm);
lookPoker("dp",four,hm);
}
/**
* 定义看牌的方法
* @param name 姓名
* @param ts 牌的编号
* @param hm 牌所对应的map集合
*/
public static void lookPoker(String name,TreeSet<Integer> ts,HashMap<Integer,String> hm) {
System.out.print(name+"的牌是");
for (Integer t : ts) {
System.out.print(hm.get(t)+" ");
}
System.out.println();
}
排序查找算法
冒泡排序
int[] arr = {5, 4, 3, 2, 1};
/*
关键点:
外层循环-1:比较的轮数是长度-1
内层循环:-1是为了防止索引越界
-i:是为了提高效率
谁和谁比较:j和j+1
*/
for (int i = 0; i < arr.length - 1; i++) {
for (int j = 0; j < arr.length - 1 - i; j++) {
if (arr[j] > arr[j + 1]) {
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
System.out.println(Arrays.toString(arr));
选择排序
int[] arr = {5, 4, 3, 2, 1};
/*
关键点:
外层循环-1:比较的轮数是长度-1
内层循环:
j=i+1:是为了提高效率
谁和谁比较:j和i
*/
for (int i = 0; i < arr.length - 1; i++) {
for (int j = i + 1; j < arr.length; j++) {
if (arr[i] > arr[j]) {
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
}
System.out.println(Arrays.toString(arr));
二分查找
<!--前提,元素有序,可以不连续-->
int[] arr = {5, 4, 3, 2, 1};
// 定义要查找的数据
int target = 6;
// 定义开始索引
int start = 0;
// 定义结束索引
int end = arr.length - 1;
while (start <= end) {
// 定义中间索引
int mid = (start + end) / 2;
if (arr[mid] > target) {
end = mid - 1;
} else if (arr[mid] < target) {
start = mid + 1;
} else {
System.out.println(mid);
}
}
异常
异常:就是程序出现了不正常的情况
Throwable:异常的顶级父类
Error:严重问题,不需要处理,一般为硬件问题
Exception:称为异常类,他表示程序本身可以处理的问题
RuntimeException:在编译期是不检查的,出现问题后,需要我们回来修改代码
非RuntimeException:编译期就必须处理的,否则程序不能通过变异,就更不能正常运行了
虚拟机默认处理异常的方式:
将异常类名,信息和位置,打印到控制台,程序停止运行
异常处理之throws
格式:
throws 异常类名
注意:这个格式是跟在方法的括号后面的
编译时异常必须要进行处理,两种处理方案:try...catch或者throws,如果采用throws这种方案,将来谁来调用谁处理
运行时异常可以不处理,出现问题后,需要我们回来修改代码
异常处理之try...catch
IDEA快捷键:option+command+t
程序从try里面的代码开始执行
出现异常,会自动生成一个异常类对象,该异常类对象将被提交给Java运行时系统
当Java运行时系统接收到异常对象时,会到catch中去找匹配的异常类,找到后进行异常的处理
执行完毕后,程序还可以继续往下执行
// 格式 try { 可能出现异常的代码; } catch(异常类名 变量名) { 异常的处理代码; }
try {
int[] arr = {1,2,3};
System.out.println(arr[3]);
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println("你访问的数组的索引不存在");
e.printStackTrace();
}
异常处理之try..catch...finally
finally:在异常处理时提供finally块来执行所有清除操作.比如IO流中的释放资源
特点:被finally控制的语句一定会执行,除非JVM退出
//格式
try{
可能出现异常的代码;
}catch(异常名 变量名){
异常的处理代码;
}finally{
执行所有清除操作;
}
// 异常抓取
FileOutputStream fos = null;
try {
fos = new FileOutputStream("/Users/zhumingli/Desktop/txt/java.txt", true);
for (int i = 0; i < 10; i++) {
fos.write("hello".getBytes());
fos.write("\r\n".getBytes());
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fos != null) {
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
// JDK7之后针对流的异常改进方案
try(定义流对象){
可能出现异常的代码;
}catch(异常类名 变量名){
异常的处理代码;
}
资源会自动释放
//JDK9之后的异常改进方案
定义输入流对象;
定义输出流对象;
try(输入流对象;输出流对象){
可能出现异常的代码
}catch(异常类名 变量名){
异常的处理代码;
}
资源会自动释放
Throwable的成员方法
方法名 | 说明 |
---|---|
public String getMessage() | 返回此throwable的详细消息字符串 |
public String toString() | 返回此 可抛出的简短描述 |
public void printStackTrace() | 把异常的错误信息输出在控制台 |
自定义异常
// 创建异常类继承Exception,并给出构造方法
public class ScoreException extends Exception{
public ScoreException() {
}
public ScoreException(String message) {
super(message);
}
}
// 测试类进行测试
public class ScoreTest {
public static void main(String[] args) throws ScoreException {
Scanner sc = new Scanner(System.in);
System.out.println("请输入你的分数");
int i = sc.nextInt();
if (i > 100 || i < 0) {
// throw new ScoreException();
throw new ScoreException("分数应该在0-100之间");
} else {
System.out.println("分数正常");
}
}
}
throws和throw的区别
throws | throw |
---|---|
用在方法声明后面,跟的是异常类名 | 用在方法体内,跟的是异常对象名 |
表示抛出异常,由该方法的调用者来处理 | 表示抛出异常,有方法体内的语句处理 |
表示出现异常的一种可能性,并不一定会发生这些异常 | 执行throw一定抛出了某种异常 |