数据结构
数据结构 = 存放数据的容器 + 数据的排列,存取规则
java日常开发只用两大类:
- 数组
- 集合(List、Set、Map)
1. 数组 Array
特点:长度固定,一旦定义不能更改;只能存储同一种数据类型(基本类型/引用类型),在内存中占用连续的存储空间;通过索引快速访问元素;属于引用类型
java
//数据一旦初始化,长度就已经定死了,无法更改
public class ArrayDemo {
public static void main(String[] args) {
// 声明并创建长度为 3 的 int 数组
int[] arr = new int[3];
// 可以赋值
arr[0] = 10;
arr[1] = 20;
arr[2] = 30;
// 报错!数组越界,因为长度固定为3,不能访问索引3
// arr[3] = 40;
System.out.println("数组长度:" + arr.length); // 输出:3
}
}
//数组是类型安全的容器,只能存好定义好的类型
public class ArrayType {
public static void main(String[] args) {
// int 数组,只能存整数
int[] nums = {1,2,3};
// nums[0] = "Java"; 报错!不能存字符串
// String 数组,只能存字符串
String[] names = new String[2];
names[0] = "张三";
// names[1] = 100; 报错!
}
}
//通过数组名【索引】快速读写,效率极高
public class ArrayIndex {
public static void main(String[] args) {
// 静态初始化
String[] fruits = {"苹果", "香蕉", "橙子"};
// 访问:索引 0 是第一个元素
System.out.println(fruits[0]); // 苹果
System.out.println(fruits[2]); // 橙子
// 修改元素
fruits[1] = "葡萄";
System.out.println(fruits[1]); // 葡萄
}
}
//创建数组时,未手动赋值的元素会自动赋默认值
int → 0
double → 0.0
boolean → false
引用类型(String / 对象)→ null
public class ArrayDefault {
public static void main(String[] args) {
// 只指定长度,不赋值
int[] arr = new int[3];
System.out.println(arr[0]); // 0
System.out.println(arr[1]); // 0
boolean[] boolArr = new boolean[2];
System.out.println(boolArr[0]); // false
String[] strArr = new String[2];
System.out.println(strArr[0]); // null
}
}
//数组变量是引用,指向堆内存中的数组对象
public class ArrayRef {
public static void main(String[] args) {
// arr1 是引用,指向堆内存的数组对象
int[] arr1 = {10,20};
// 把引用赋值给 arr2,两个引用指向同一个数组
int[] arr2 = arr1;
arr2[0] = 100; // 修改 arr2,arr1 也会变
System.out.println(arr1[0]); // 100
}
}
//因为长度固定,用for/增强for遍历最方便:
public class ArrayLoop {
public static void main(String[] args) {
int[] arr = {1,2,3,4,5};
// 普通 for 循环(通过索引)
for(int i=0; i<arr.length; i++){
System.out.print(arr[i] + " ");
}
System.out.println();
// 增强 for 循环(直接取元素)
for(int num : arr){
System.out.print(num + " ");
}
}
}
缺点 :长度固定,不够用只能新建数组复制,很麻烦 → 所以有 ArrayList
2. List 列表(有序、可重复)
List 核心特点
- 有序:存入顺序和取出顺序一致
- 可重复:允许存放重复元素
- 有索引 :可以通过下标
0,1,2...操作元素 - 长度可变:动态扩容,不用像数组一样固定长度
- 支持泛型:可以限制存放指定类型,避免类型转换异常
- 常用实现类 :
ArrayList:查询快、增删慢,日常最常用LinkedList:增删快、查询慢,适合频繁插入删除Vector:线程安全,效率低,基本不用
常用List实现类区别
| 类 | 底层结构 | 查询 | 增删 | 线程安全 |
|---|---|---|---|---|
| ArrayList | 动态数组 | 快 | 慢 | 不安全 |
| LinkedList | 双向链表 | 慢 | 快 | 不安全 |
| Vector | 动态数组 | 一般 | 慢 | 安全 |
开发 90% 场景直接用 ArrayList
基础使用代码举例:
java
//1. 创建 List、添加元素
import java.util.ArrayList;
import java.util.List;
public class ListDemo1 {
public static void main(String[] args) {
// 泛型<String>:只能存字符串
List<String> list = new ArrayList<>();
// 添加元素
list.add("张三");
list.add("李四");
list.add("王五");
list.add("张三"); // List允许重复
System.out.println(list); // [张三, 李四, 王五, 张三]
}
}
//2. 常用增删改查方法
import java.util.ArrayList;
import java.util.List;
public class ListDemo2 {
public static void main(String[] args) {
List<Integer> list = new ArrayList<>();
// 1. 添加
list.add(10);
list.add(20);
list.add(30);
System.out.println("添加后:" + list);
// 2. 根据索引插入
list.add(1, 99);
System.out.println("索引1插入99:" + list);
// 3. 根据索引获取元素
Integer num = list.get(2);
System.out.println("索引2元素:" + num);
// 4. 修改元素
list.set(2, 88);
System.out.println("修改索引2:" + list);
// 5. 根据索引删除
list.remove(1);
System.out.println("删除索引1:" + list);
// 6. 根据元素删除
list.remove(Integer.valueOf(30));
System.out.println("删除元素30:" + list);
// 7. 获取集合大小
System.out.println("元素个数:" + list.size());
// 8. 判断是否包含元素
System.out.println("是否包含10:" + list.contains(10));
// 9. 清空集合
// list.clear();
}
}
//list四种遍历方式(比掌握)
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class ListForEach {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("Java");
list.add("Python");
list.add("C++");
// 方式1:普通for循环(带索引)
System.out.println("===== 普通for =====");
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}
// 方式2:增强for循环(最常用)
System.out.println("===== 增强for =====");
for (String s : list) {
System.out.println(s);
}
// 方式3:迭代器 Iterator
System.out.println("===== 迭代器 =====");
Iterator<String> it = list.iterator();
while (it.hasNext()) {
System.out.println(it.next());
}
// 方式4:Lambda 表达式(JDK8+)
System.out.println("===== Lambda =====");
list.forEach(System.out::println);
}
}
//LinkedList 特有用法
import java.util.LinkedList;
public class LinkedListDemo {
public static void main(String[] args) {
LinkedList<String> link = new LinkedList<>();
// 首尾添加
link.addFirst("第一个");
link.addLast("最后一个");
link.add("中间");
System.out.println(link);
// 获取首尾元素
System.out.println("首元素:" + link.getFirst());
System.out.println("尾元素:" + link.getLast());
// 删除首尾
link.removeFirst();
link.removeLast();
System.out.println("删除首尾后:" + link);
}
}
//list存放自定义对象
import java.util.ArrayList;
import java.util.List;
// 自定义实体类
class User {
private String name;
private int age;
public User(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "User{name='" + name + "', age=" + age + "}";
}
}
public class ListObject {
public static void main(String[] args) {
List<User> userList = new ArrayList<>();
// 添加自定义对象
userList.add(new User("小明", 20));
userList.add(new User("小红", 18));
// 遍历
for (User user : userList) {
System.out.println(user);
}
}
}
Set集合
特点:set是java.util下的集合接口,继承collecttion
三大核心特征:
- 无序 :存取顺序不一致(部分实现类例外:
LinkedHashSet有序) - 不可重复 :不能存储重复元素,自动去重
- 无索引 :没有下标 ,不能用普通
for循环根据索引遍历
常用三大实现类
HashSet:底层哈希表,无序、不重复、查询最快,开发最常用LinkedHashSet:底层哈希表 + 链表,有序(存取顺序一致)、不重复TreeSet:底层红黑树,自然排序 / 自定义排序、不重复
**1.**HashSet
java
//1.HashSet(开发最常用)
//特点:无序,不可重复,无索引
import java.util.HashSet;
import java.util.Set;
public class HashSetTest {
public static void main(String[] args) {
// 创建集合
Set<String> set = new HashSet<>();
// 添加元素
set.add("张三");
set.add("李四");
set.add("王五");
// 添加重复元素
set.add("张三");
// 打印:无序 + 自动去重
System.out.println(set);
}
}
//打印结果为[李四, 张三, 王五]
//HashSet 常用方法
import java.util.HashSet;
import java.util.Set;
public class HashSetMethod {
public static void main(String[] args) {
Set<Integer> set = new HashSet<>();
// 添加
set.add(10);
set.add(20);
set.add(10); // 重复,无效
// 集合元素个数
System.out.println("元素个数:" + set.size());
// 判断是否包含某个元素
System.out.println("是否包含20:" + set.contains(20));
// 删除元素
set.remove(10);
System.out.println("删除后:" + set);
// 判断是否为空
System.out.println("是否为空:" + set.isEmpty());
// 清空所有元素
// set.clear();
}
}
//Set遍历方式
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
public class SetForeach {
public static void main(String[] args) {
Set<String> set = new HashSet<>();
set.add("Java");
set.add("MySQL");
set.add("Vue");
// 1. 增强for循环(最常用)
for (String s : set) {
System.out.println(s);
}
System.out.println("--------");
// 2. 迭代器遍历
Iterator<String> it = set.iterator();
while (it.hasNext()) {
System.out.println(it.next());
}
System.out.println("--------");
// 3. Lambda 遍历(JDK8+)
set.forEach(System.out::println);
}
}
2.LinkedHashSet
特点:保留添加顺序,不可重复;
适合:既要去重,又要保证存入顺序和取出顺序一致
java
import java.util.LinkedHashSet;
import java.util.Set;
public class LinkedHashSetTest {
public static void main(String[] args) {
Set<String> set = new LinkedHashSet<>();
set.add("苹果");
set.add("香蕉");
set.add("橙子");
set.add("苹果"); // 重复自动忽略
// 输出顺序 = 添加顺序
System.out.println(set);
}
}
//输出 [苹果, 香蕉, 橙子]
3.TreeSet 自动排序集合
特点:不重复,默认升序排序
java
import java.util.TreeSet;
import java.util.Set;
public class TreeSetTest {
public static void main(String[] args) {
Set<Integer> set = new TreeSet<>();
set.add(5);
set.add(1);
set.add(9);
set.add(1);
// 自动从小到大排序
System.out.println(set);
}
}
//输出[1, 5, 9]
Map 键值对
特点:键唯一,值可重复;通过key快速找value;无索引,不能用普通for循环遍历
开发用得最多:接口传参,存配置,字典,下拉选项
常用实现类:
- HashMap:无序、键唯一、效率最高(日常最常用)
- LinkedHashMap :有序、键唯一
- TreeMap:按键自动排序
| 实现类 | 特点 |
|---|---|
| HashMap | 存取无序、键不重复、效率高 |
| LinkedHashMap | 存取有序、键不重复 |
| TreeMap | 按键自然排序、键不重复 |
HashMap
java
//map基础使用(增删改查)
import java.util.HashMap;
import java.util.Map;
public class MapDemo1 {
public static void main(String[] args) {
// Map<键类型, 值类型>
Map<String, Integer> map = new HashMap<>();
// 1. 添加键值对 put(key,value)
map.put("张三", 20);
map.put("李四", 22);
map.put("王五", 19);
// 键重复,会覆盖原来的值
map.put("张三", 25);
// 打印整个Map
System.out.println(map);
// 2. 根据键获取值 get(key)
Integer age = map.get("李四");
System.out.println("李四年龄:" + age);
// 3. 根据键删除键值对 remove(key)
map.remove("王五");
System.out.println("删除王五后:" + map);
// 4. 判断是否包含指定键 / 值
System.out.println("是否包含键 张三:" + map.containsKey("张三"));
System.out.println("是否包含值 22:" + map.containsValue(22));
// 5. 获取键值对个数
System.out.println("元素个数:" + map.size());
// 6. 清空集合
// map.clear();
// 7. 判断是否为空
System.out.println("是否为空:" + map.isEmpty());
}
}
//Map三种遍历方式(必掌握)
//方式1:先获取所有键,再通过键取值
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
public class MapLoop1 {
public static void main(String[] args) {
Map<String, Integer> map = new HashMap<>();
map.put("Java", 80);
map.put("Python", 90);
map.put("C++", 70);
// 获取所有键,放到 Set 集合
Set<String> keySet = map.keySet();
// 遍历所有键
for (String key : keySet) {
// 通过键拿值
Integer value = map.get(key);
System.out.println(key + " = " + value);
}
}
}
//方式2:键值对对象遍历(推荐)
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
public class MapLoop2 {
public static void main(String[] args) {
Map<String, Integer> map = new HashMap<>();
map.put("语文", 95);
map.put("数学", 98);
map.put("英语", 92);
// 获取所有键值对对象
Set<Map.Entry<String, Integer>> entrySet = map.entrySet();
for (Map.Entry<String, Integer> entry : entrySet) {
String key = entry.getKey();
Integer value = entry.getValue();
System.out.println(key + " : " + value);
}
}
}
//Lambda 遍历(JDK8 简洁写法)
import java.util.HashMap;
import java.util.Map;
public class MapLoop3 {
public static void main(String[] args) {
Map<String, String> map = new HashMap<>();
map.put("001", "小明");
map.put("002", "小红");
// 一行遍历
map.forEach((k, v) -> System.out.println(k + "->" + v));
}
}
LinkedHashMap
保留添加顺序,键不重复
java
import java.util.LinkedHashMap;
import java.util.Map;
public class LinkedHashMapDemo {
public static void main(String[] args) {
Map<String, Integer> map = new LinkedHashMap<>();
map.put("A", 1);
map.put("B", 2);
map.put("C", 3);
// 输出顺序和添加顺序一致
System.out.println(map);
}
}
TreeMap
按键默认升序排列
java
import java.util.TreeMap;
import java.util.Map;
public class TreeMapDemo {
public static void main(String[] args) {
Map<String, Integer> map = new TreeMap<>();
map.put("b", 2);
map.put("a", 1);
map.put("c", 3);
// 按键字典顺序自动排序
System.out.println(map);
}
}
Map 存储自定义对象示例
java
import java.util.HashMap;
import java.util.Map;
class User {
private String name;
private int age;
public User(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "User{name='" + name + "', age=" + age + "}";
}
}
public class MapUserDemo {
public static void main(String[] args) {
Map<String, User> userMap = new HashMap<>();
userMap.put("user1", new User("张三", 20));
userMap.put("user2", new User("李四", 22));
// 遍历
userMap.forEach((k, v) -> System.out.println(k + " : " + v));
}
}
链表 LinkedList
由一个个节点连起来,每个节点存【数据 + 下一个节点地址】,内存不连续,不用固定长度,插入删除极快。
数组:内存连续,靠下标访问。
链表:内存不连续,每个节点存数据+下一个节点地址
常见链表分类
- 单向链表:只能从头往后走
- 双向链表 :可以向前、向后遍历(Java
LinkedList就是双向链表) - 循环链表:最后一个节点指向头节点
| 特性 | 数组 | 链表 |
|---|---|---|
| 内存 | 连续 | 不连续 |
| 查询 | 快(按索引 O (1)) | 慢(从头遍历 O (n)) |
| 增删 | 慢(要移动元素) | 快(只改引用指向) |
| 长度 | 固定不可变 | 动态不限长度 |
| 浪费空间 | 可能浪费 | 每个节点多占指针空间 |
一句话:查多用数组 / ArrayList,频繁增删用链表 / LinkedList
java
//基本使用代码
import java.util.LinkedList;
public class LinkTest {
public static void main(String[] args) {
// 创建链表
LinkedList<String> list = new LinkedList<>();
// 普通添加
list.add("A");
list.add("B");
list.add("C");
// 头部、尾部添加
list.addFirst("开头");
list.addLast("结尾");
System.out.println(list);
// 获取首尾
System.out.println("第一个:" + list.getFirst());
System.out.println("最后一个:" + list.getLast());
// 删除首尾
list.removeFirst();
list.removeLast();
System.out.println("操作后:" + list);
}
}
//常用方法
add() // 尾部加
addFirst() // 头部加
addLast() // 尾部加
getFirst() // 拿第一个
getLast() // 拿最后一个
removeFirst() // 删第一个
removeLast() // 删最后一个
size() // 元素个数
//遍历链表
// 增强for遍历
for (String s : list) {
System.out.println(s);
}
栈 Stack / 队列 Queue
栈 Stack
特点:最后进去的,最先出来;
常用场景:函数调用,递归,括号匹配,浏览器后退,撤销操作。
java
import java.util.LinkedList;
// 用 LinkedList 当栈用
public class StackDemo {
public static void main(String[] args) {
LinkedList<String> stack = new LinkedList<>();
// 入栈(压栈)
stack.push("第1个");
stack.push("第2个");
stack.push("第3个");
System.out.println(stack);
// 出栈:后进先出
System.out.println(stack.pop()); // 第3个
System.out.println(stack.pop()); // 第2个
// 查看栈顶元素,不删除
System.out.println(stack.peek()); // 第1个
}
}
队列:先进先出
java
import java.util.Queue;
import java.util.LinkedList;
public class QueueDemo {
public static void main(String[] args) {
Queue<String> queue = new LinkedList<>();
queue.offer("一号");
queue.offer("二号");
System.out.println(queue.poll()); // 先出一号
}
}