学习目标
List集合
数据结构
Set接口
Collections
一、List接口
1、概述
java.util.List 接口继承自Collection 接口、有序可重复
List接口实现类、ArrayList、LinkedList
常用方法:
public void add(int index, E element) : 将指定的元素,添加到该集合中的指定位置上。
public E get(int index) :返回集合中指定位置的元素。
public E remove(int index) : 移除列表中指定位置的元素, 返回的是被移除的元素。
public E set(int index, E element) :用指定元素替换集合中指定位置的元素,返回值的更新前的元素。
演示:
package com.blog.test;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
public class Test {
public static void main(String[] args) {
// 创建List集合对象
List<String> list = new ArrayList<String>();
// 往 尾部添加 指定元素
list.add("小亚");
list.add("小天");
list.add("不高兴");
System.out.println(list);
// add(int index,String s) 往指定位置添加
list.add(1,"没头脑");
System.out.println(list);
// String remove(int index) 删除指定位置元素 返回被删除元素
// 删除索引位置为2的元素
System.out.println("删除索引位置为2的元素");
System.out.println(list.remove(2));
System.out.println(list);
list.set(0, "三毛");
System.out.println(list);
// String get(int index) 获取指定位置元素
// 跟size() 方法一起用 来 遍历的
for(int i = 0;i<list.size();i++){
System.out.println(list.get(i));
}
//还可以使用增强for
for (String string : list) {
System.out.println(string);
}
}
}
2、ArrayList
说明:java.util.ArrayList 集合数据存储的结构是数组结构。
特点:增删慢,查找快,
常用方法:
public void add(int index, E element) : 将指定的元素,添加到该集合中的指定位置上。
public E get(int index) :返回集合中指定位置的元素。
public E remove(int index) : 移除列表中指定位置的元素, 返回的是被移除的元素。
public E set(int index, E element) :用指定元素替换集合中指定位置的元素,返回值的更新前的元素。
package com.blog.test;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
public class Test {
public static void main(String[] args) {
// List 接口接收.
List<String> list = new ArrayList<String>();
list.add("张三");
list.add("李四");
list.add("王五");
// 1. add(int index, E e);
// 需求 : 将 `赵六` 添加到张三后面
list.add(1, "赵六");
// 2. remove(int index);
// 需求 : 删除王五
list.remove(3);
// 3. set(int index, E e);
// 需求 : 将李四修改为田七
list.set(2, "田七");
// 4. get(int index);
// 需求 : 查询 1 元素
String name = list.get(1);
System.out.println(name);
System.out.println("list = " + list);
// 遍历 List 集合 (size + get)
for (int i = 0; i < list.size(); i++) {
String n = list.get(i);
System.out.println("n = " + n);
}
}
}
3、LinkedList
说明:java.util.LinkedList 集合数据存储的结构是双向链表结构。增删快、查询慢
头尾操作:
常用方法:
1. addFirst(E e); 将元素添加到链表的开头.
2. addLast(E e); 将元素添加到链表的结尾.
3. getFirst(E e); 获取链表头部的元素.
4. getLast(E e); 获取链表的尾部元素.
5. removeFirst(E e); 删除链表的头部元素.
6. removeLast(E e); 删除链表的尾部元素.
7. public E pop() :从此列表所表示的堆栈处弹出一个元素。
8. public void push(E e) :将元素推入此列表所表示的堆栈。
9. public boolean isEmpty() :如果列表不包含元素,则返回true。
演示:
package com.blog.test;
import java.util.LinkedList;
public class Test {
public static void main(String[] args) {
LinkedList<String> link = new LinkedList<String>();
//添加元素
link.addFirst("abc1");
link.addFirst("abc2");
link.addLast("abc3");
System.out.println(link);
// 获取元素
System.out.println(link.getFirst());
System.out.println(link.getLast());
// 删除元素
System.out.println(link.removeFirst());
System.out.println(link.removeLast());
System.out.println(" = ==");
while (!link.isEmpty()) { //判断集合是否为空
System.out.println(link.pop()); //弹出集合中的栈顶元素
}
System.out.println(link);
}
}
二、Java中的数据结构
1、线性表
线性表:`内存地址连续`、ArrayList 数组列表集合底层就是数组结构. 数组结构也被称为 `线性表`
特点:增删慢,查找快
2、单链表结构
单链表:由一系列结点node组成,一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域
特点:查询慢 增删快
HashSet:为单链表(底层是由HashMap支持的)
3、双链表结构
双链表:由一系列结点node组成,
一个是存储上一个结点的指、
一个是存储数据元素的数据域,
另一个是存储下一个结点地址的指针域
图解:LinkedList为双链表结构
4、栈结构(先进后出)
栈:stack,又称堆栈,它是运算受限的线性表,仅允许在一端进行插入和删除操作
压栈:push、就是存元素。即,把元素存储到栈的顶端位置,栈中已有元素依次向栈底方向移动一个位置。
弹栈:pop、就是取元素。即,把栈的顶端位置元素取出,栈中已有元素依次向栈顶方向移动一个位置。
5、队列结构(先进先出)
队列:queue,它同堆栈一样,也是受限的线性表,仅允许在表的一端进行插入,另一端进行删除。
package com.blog.test;
import java.util.LinkedList;
public class Test {
public static void main(String[] args) {
LinkedList<String> link = new LinkedList<String>();
link.offer("aaa");
link.offer("bbb");
link.offer("ccc");
System.out.println("link = " + link);
while (!link.isEmpty()){
String poll = link.poll();
System.out.println("poll = " + poll);
}
}
}
6、红黑树结构
二叉树:binary tree ,是每个结点不超过2的有序树(tree)TreeSet集合
三、Set接口
1、概述
java.util.Set 接口和java.util.List 接口一样,同样继承自Collection 接口
Set 接口中元素,无序,不重复。
Set 集合有多个子类,这里我们介绍其中的java.util.HashSet 、java.util.LinkedHashSet
2、HashSet
java.util.HashSet 是Set 接口的一个实现类,无序,不重复
java.util.HashSet 底层的实现其实是一个java.util.HashMap 支持
public class HashSetDemo {
public static void main(String[] args) {
//创建 Set集合
HashSet<String> set = new HashSet<String>();
//添加元素
set.add(new String("cba"));
set.add("abc");
set.add("bac");
set.add("cba");
//遍历
for (String name : set) {
System.out.println(name);
}
}
}
HashSet集合存储结构是哈希表、JDK1.8以后、希表存储采用数组+链表+红黑树实现,当链表长度超过阈值(8)时,将链表转换为红黑树
3、 需求 : 存储自定义对象
说明:只要想 Hash 表中存储自定义对象, 那么该自定义对象所属的类就必须重写 `hashCode + equals` 方法.
类必须重写 Object 类的 hashCode 方法, 实现根据 Student 对象的数据内容来计算哈希值, 如果数据相同, 计算的哈希值就应该相同.
关于 HashCode 方法的说明 :
public class HashCodeStudentTest2 {
public static void main(String[] args) {
int hashCode1 = new Student("张三", 18).hashCode();
int hashCode2 = new Student("张三", 18).hashCode();
System.out.println("hashCode1 = " + hashCode1);
System.out.println("hashCode2 = " + hashCode2);
// 请问 : 为什么 String 类计算出来的 `哈希值` 是一样的 ???
// 解答 : 因为 String 类重写了 Object 类的 `hashCode` 方法.
int hashCode3 = "张三".hashCode();
int hashCode4 = "张三".hashCode();
System.out.println("hashCode3 = " + hashCode3);
System.out.println("hashCode4 = " + hashCode4);
int hashCode5 = new String("张三").hashCode();
int hashCode6 = new String("张三").hashCode();
System.out.println("hashCode5 = " + hashCode5);
System.out.println("hashCode6 = " + hashCode6);
}
}
手动重写 hashCode 与 equals 方法 :
// hashCode 方法 (数据相同, 哈希值就应该相同)
@Override
public int hashCode() {
// 数据 (name, age)
return age + name.hashCode(); // age + 字符串哈希方法的返回值 18 + 774889 -> 774907
}
// equals 方法
@Override
public boolean equals(Object obj) {
// System.out.println("Student 类的 equals 方法被调用..."); 测试
if (this == obj) return true;
if (obj == null || getClass() != obj.getClass()) return false;
Student stu = (Student) obj;
return age == stu.age && Objects.equals(name, stu.name);
}
Student 类的完整代码 :
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 int hashCode() {
return Objects.hash(name, age);
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + 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;
}
}
测试代码:
public class HashSetStudentTest {
public static void main(String[] args) {
HashSet<Student> set = new HashSet<>();
// 存储元素
set.add(new Student("张三", 18));
set.add(new Student("李四", 28));
set.add(new Student("王五", 19));
set.add(new Student("赵六", 20));
set.add(new Student("张三", 18));
set.add(new Student("李四", 28));
set.add(new Student("王五", 19));
set.add(new Student("赵六", 20));
// 遍历
for (Student stu : set) {
System.out.println(stu);
}
}
}
4、LinkedHashSet
具有可预知迭代顺序的 Set 接口的哈希表和链接列表实现。
public class LinkedHashSetDemo {
public static void main(String[] args) {
Set<String> set = new LinkedHashSet<String>();
set.add("bbb");
set.add("aaa");
set.add("abc");
set.add("bbc");
Iterator<String> it = set.iterator();
while (it.hasNext()) {
System.out.println(it.next());
}
}
}
结果:
bbb
aaa
abc
bbc
5、可变参数
格式:
修饰符 返回值类型 方法名(参数类型... 形参名){ }
public class VariableArgumentsTest1 {
public static void main(String[] args) {
int[] arr = {10, 20, 30, 40, 50};
int sum = getSum(arr);
System.out.println("sum = " + sum);
int sum1 = getSum(10, 20, 30, 40, 50);
System.out.println("sum1 = " + sum1);
}
// 需求 : 定义一个方法, 获取多个整形的累加和
// 说明 : 可变参数底层就是 `数组`.
// 注意点 : 可变参数必须是参数列表的 `最后一个参数`.
// Vararg (可变参数) parameter (形参) must be the last in the list (在参数列表中)
public static int getSum(int... array) {
int sum = 0;
for (int i = 0; i < array.length; i++) {
sum += array[i];
}
return sum;
}
/*public static int getSum(int[] array) {
int sum = 0;
for (int i = 0; i < array.length; i++) {
sum += array[i];
}
return sum;
}*/
}
四、Collections集合工具类
1、概述
java.utils.Collections 是集合工具类、工具类中的方法基本全部都是静态的, 因此, 直接可以实用类名调用.
2、常用方法
1. shuffle(List list); 将集合中的元素进行随机置换.
2. addAll(Collection<? super T> c, T… elements); 将可变参数的元素添加指定的集合中.
3. max(Collection c); 获取集合中元素的最大值.
4. min(Collection c); 获取集合中元素的最小值.
5. reverse(Collection c); 集合中元素的反转.
6. sort(List<T extends Comparables> list); 按照元素的 `自然规则` 规则实现元素排序.
7. sort(List<T> list, Comparator<T> c); 按照元素的 `比较器规则` 规则实现元素排序.
演示:
package com.blog.test;
import java.util.ArrayList;
import java.util.Collections;
public class Test {
public static void main(String[] args) {
ArrayList<Integer> list = new ArrayList<>();
// 1. addAll(Collection<? super T> c, T… elements); 将可变参数的元素添加指定的集合中.
Collections.addAll(list, 10, 20, 30, 40, 50, 60, 70, 80, 90);
System.out.println("list = " + list);
// 2. shuffle(List list); 将集合中的元素进行随机置换.
Collections.shuffle(list);
System.out.println("list = " + list);
ArrayList<Integer> list2 = new ArrayList<>();
Collections.addAll(list2, 66, 967, 34, 45, 44, 55);
System.out.println("list2 = " + list2);
// 3. max(Collection c); 获取集合中元素的最大值.
Integer max = Collections.max(list2);
System.out.println("max = " + max);
// 4. min(Collection c); 获取集合中元素的最小值.
Integer min = Collections.min(list2);
System.out.println("min = " + min);
// 5. sort 排序
// [33, 55, 66, 77, 88, 99]
// 比较 : Integer 自然排序 (Comparable<Integer>)
Collections.sort(list2); // 排序: 底层就是集合中元素交互.
System.out.println("list2 = " + list2);
// 6. reverse(Collection c); 集合中元素的反转.
Collections.reverse(list2);
System.out.println("list2 = " + list2);
}
}
排序底层实现的交互 :
3、Comparable自然排序
字符串的自然排序 : 字典顺序 (a ~ z)
说明 : 要求 List 集合中的 T 元素类型, 必须继承自 Comparable 接口. 之前使用 Integer, String 类不报错, 因为这两个类继承了 Comparable 接口.
package com.blog.test;
import java.util.ArrayList;
import java.util.Collections;
public class Test {
public static void main(String[] args) {
// 查看字符串本身的排序规则 : (自然排序) Comparable<String>
ArrayList<String> list = new ArrayList<>();
list.add("Lucy");
list.add("Tom");
list.add("Java");
list.add("Jack");
list.add("php");
list.add("Jerry");
list.add("linux");
list.add("TeBaoBao");
// 排序
Collections.sort(list);
for (String name : list) {
System.out.println(name);
}
}
}
4、Comparator比较器排序
字符串的自定义排序 :
说明 : 此方法对集合中的 T 类型没有要求, 但是, 第二个参数需要传递一个 Comparator 接口类型的参数.
package com.blog.test;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
public class Test {
public static void main(String[] args) {
// 查看字符串本身的排序规则 : (自然排序) Comparable<String>
ArrayList<String> list = new ArrayList<>();
list.add("Lucy");
list.add("Tom");
list.add("Java");
list.add("Jack");
list.add("php");
list.add("Jerry");
list.add("linux");
list.add("TeBaoBao");
// 比较器排序 / 整体排序
// 第二个参数类型为 : Comparator 接口类型
Collections.sort(list, new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
/*
结果1 : o1 比 o2 对象大. 正数 (小 -> 大)
结果2 : o1 比 o2 对象小. 负数 (大 -> 小)
结果2 : o1 和 o2 相等. 原来的顺序
*/
// 字符串长度 : o1.length() - o2.length();
return o1.length() - o2.length();
}
});
for (String name : list) {
System.out.println(name);
}
}
}
5、自定义排序
说明 : 创建一个学生类,存储到ArrayList集合中完成指定排序操作。
实现方式一 : 自然排序
Student 类 :
public class Student implements Comparable<Student> {
// 属性
private String name;
private int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + 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 int compareTo(Student o) {
// 规则 : 1. 先按年龄 (从小到大) 2. 如果年龄相同 (姓名, 自然排序)
// 步骤一 : 年龄
int result = this.age - o.age;
// 步骤二 : 判断年龄是否相同
if (result == 0) {
// 步骤三 : 根据姓名, 实现自然排序 (String -> Comparable 接口中的抽象方法为 compareTo)
result = this.name.compareTo(o.name);
}
// 步骤四 : 返回结果
return result;
}
}
测试类 :
package com.blog.test;
import java.util.ArrayList;
import java.util.Collections;
public class Test {
public static void main(String[] args) {
ArrayList<Student> list = new ArrayList<>();
list.add(new Student("Jack", 18));
list.add(new Student("Rose", 19));
list.add(new Student("Peter", 16));
list.add(new Student("Ann", 15));
list.add(new Student("Tom", 18));
list.add(new Student("Jim", 19));
list.add(new Student("Lily", 16));
list.add(new Student("Jan", 15));
// 方式一 : 自然排序
Collections.sort(list);
// 遍历
for (Student stu : list) {
System.out.println(stu);
}
}
}
实现方式二 : 比较器排序
Student 类 :
package com.blog.test;
public class Student {
// 属性
private String name;
private int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + 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;
}
}
测试类 :
package com.blog.test;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
public class Test {
public static void main(String[] args) {
ArrayList<Student> list = new ArrayList<>();
list.add(new Student("Jack", 18));
list.add(new Student("Rose", 19));
list.add(new Student("Peter", 16));
list.add(new Student("Ann", 15));
list.add(new Student("Tom", 18));
list.add(new Student("Jim", 19));
list.add(new Student("Lily", 16));
list.add(new Student("Jan", 15));
// 方式二 : 比较器排序
Collections.sort(list, new Comparator<Student>() {
@Override
public int compare(Student o1, Student o2) {
int result = o1.getAge() - o2.getAge();
if (result == 0) {
result = o1.getName().compareTo(o2.getName());
}
return result;
}
});
// 遍历
for (Student stu : list) {
System.out.println(stu);
}
}
}