当前位置: 首页 > news >正文

尚硅谷2019版Java集合和泛型

第十一章 Java集合框架

集合框架全景图

mindmap
    root((Java集合))
        Collection单列
            List有序可重复
                ArrayList
                LinkedList
                Vector
            Set无序唯一
                HashSet
                LinkedHashSet
                TreeSet
        Map双列
            HashMap
            LinkedHashMap
            TreeMap
            Hashtable
                Properties
        Tools
            Collections
            Arrays

三大核心接口对比

特性ListSetMap
元素顺序插入顺序无顺序Key唯一无序
元素重复性允许重复不允许重复Key唯一,Value可重复
常用实现类ArrayList/LinkedListHashSet/TreeSetHashMap/TreeMap
线程安全实现CopyOnWriteArrayListConcurrentSkipListSetConcurrentHashMap

ArrayList vs LinkedList

选择List实现
频繁随机访问?
ArrayList
频繁插入删除?
LinkedList
特性ArrayListLinkedList
底层结构动态数组双向链表
随机访问速度O(1)O(n)
头尾操作效率尾部快,头部慢头尾都快
内存占用连续空间,节省节点存储,占用更多
扩容机制1.5倍扩容无扩容概念

HashMap深度解析

JDK1.8+ 存储结构

graph LR
    A[数组] --> B[链表]
    A --> C[红黑树]
    B -->|长度>8 & 数组长度≥64| C

核心参数

static final int DEFAULT_INITIAL_CAPACITY = 16; // 初始容量
static final float DEFAULT_LOAD_FACTOR = 0.75f; // 负载因子
static final int TREEIFY_THRESHOLD = 8;         // 树化阈值
static final int UNTREEIFY_THRESHOLD = 6;       // 链化阈值

哈希冲突解决

// 经典扰动函数(JDK1.8)
static final int hash(Object key) {
    int h;
    return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}

集合线程安全解决方案

实现方式示例特点
同步包装Collections.synchronizedList方法级synchronized锁
CopyOnWriteCopyOnWriteArrayList写时复制,读无锁
CAS机制ConcurrentHashMap分段锁/Node+CAS
并发队列LinkedBlockingQueue双锁设计,put/take分开

集合工具类妙用

Collections魔法方法

// 创建不可变集合
List<String> unmodifiableList = Collections.unmodifiableList(list);

// 排序+二分查找
Collections.sort(list);
int index = Collections.binarySearch(list, "key");

// 频率统计
int count = Collections.frequency(list, "element");

性能提升技巧

// 预设集合容量(避免扩容)
ArrayList<String> list = new ArrayList<>(1000); 

// 使用批量添加
list.addAll(Arrays.asList(data)); 

// 优先迭代器遍历
for(Iterator it = list.iterator(); it.hasNext();){
    System.out.println(it.next());
}

高频面试题精解

  1. HashMap工作原理

    • 通过hashcode计算存储位置
    • JDK1.8采用数组+链表+红黑树
    • 扩容时重新计算位置(2次幂扩展)
  2. ConcurrentHashMap实现

    • JDK1.7:分段锁(Segment)
    • JDK1.8:Node+CAS+synchronized
  3. ArrayList扩容机制

    // 扩容核心代码(JDK1.8)
    int newCapacity = oldCapacity + (oldCapacity >> 1); // 1.5倍
    
  4. Iterator快速失败机制

    • modCount记录修改次数
    • 遍历时检测modCount变化

📘 实战建议

  1. 根据场景选择集合:查询多用ArrayList,增删多用LinkedList
  2. 预估数据量时显式指定初始容量(特别对HashMap)
  3. 多线程环境优先使用JUC并发集合
  4. 复杂对象作为Map键时,必须正确重写hashCode和equals
  5. 遍历时优先使用增强for循环或迭代器,避免用fori遍历链表结构
Yes
No
Yes
No
选择集合
需要键值对?
Map
需要唯一性?
Set
List

第十二章 Java泛型

泛型基础概念

泛型本质

泛型
类型安全
代码复用
编译时类型检查
减少类型转换

泛型优势

特性非泛型泛型
类型安全运行时可能ClassCastException编译时类型检查
代码简洁度需要显式类型转换自动类型推断
代码复用性针对特定类型编写一套代码处理多种类型
集合元素操作只能存储Object可存储指定类型

泛型使用场景

集合框架中的泛型

// 传统方式
List list = new ArrayList();
list.add("hello");
String str = (String) list.get(0); // 需要强制转换

// 泛型方式
List<String> list = new ArrayList<>();
list.add("hello");
String str = list.get(0); // 自动类型推断

自定义泛型类

public class Box<T> {
    private T item;
    
    public void setItem(T item) {
        this.item = item;
    }
    
    public T getItem() {
        return item;
    }
}

// 使用
Box<String> stringBox = new Box<>();
stringBox.setItem("Hello");
String str = stringBox.getItem();

泛型高级特性

泛型继承关系

Object
Number
Integer
Double
Box
Box
Box
Box

通配符使用

// 上界通配符
public static double sum(List<? extends Number> list) {
    double sum = 0;
    for (Number n : list) {
        sum += n.doubleValue();
    }
    return sum;
}

// 下界通配符
public static void addNumbers(List<? super Integer> list) {
    for (int i = 1; i <= 10; i++) {
        list.add(i);
    }
}

泛型方法

定义与使用

// 泛型方法定义
public static <T> void printArray(T[] array) {
    for (T element : array) {
        System.out.println(element);
    }
}

// 使用示例
Integer[] intArray = {1, 2, 3};
printArray(intArray);

String[] strArray = {"A", "B", "C"};
printArray(strArray);

类型推断

// 显式指定类型
Collections.<String>emptyList();

// 自动类型推断
List<String> list = Collections.emptyList();

泛型限制与注意事项

使用限制

泛型限制
不能是基本类型
不能创建泛型数组
静态成员不能使用类泛型
不能实例化类型参数

常见问题

  1. 类型擦除:泛型信息在编译后被擦除
// 编译前
List<String> list = new ArrayList<>();

// 编译后(类型擦除)
List list = new ArrayList();
  1. 泛型数组:不能直接创建
// 错误示例
// List<String>[] array = new List<String>[10];

// 正确方式
List<?>[] array = new List<?>[10];
  1. 泛型异常:不能捕获泛型异常
// 错误示例
// try { ... } catch (T e) { ... }

最佳实践建议

  1. 命名规范:使用单个大写字母作为类型参数

    • E:Element(集合元素)

    • K:Key(键)

    • V:Value(值)

    • T:Type(类型)

  2. 边界控制:合理使用上下界通配符

    • 生产者使用<? extends T>

    • 消费者使用<? super T>

  3. 类型安全:优先使用泛型集合

// 推荐
List<String> list = new ArrayList<>();

// 不推荐
List list = new ArrayList();
  1. 代码复用:善用泛型方法
public static <T> T getFirst(List<T> list) {
    return list.get(0);
}
集合泛型
自定义泛型类
泛型方法
通配符
类型擦除
项目实践

Java泛型实验

实验1:集合中使用泛型

实验目标

  • 掌握在集合中使用泛型的方法
  • 理解Comparable和Comparator接口的使用
  • 实现自定义对象的排序

实验步骤

  1. 定义Employee类
public class Employee implements Comparable<Employee> {
    private String name;
    private int age;
    private MyDate birthday;

    // 构造器、getter、setter省略

    @Override
    public int compareTo(Employee o) {
        return this.name.compareTo(o.name);
    }

    @Override
    public String toString() {
        return "Employee{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", birthday=" + birthday +
                '}';
    }
}
  1. 定义MyDate类
public class MyDate {
    private int year;
    private int month;
    private int day;

    // 构造器、getter、setter省略
}
  1. 实现排序
// 按姓名排序(自然排序)
TreeSet<Employee> set1 = new TreeSet<>();
set1.add(new Employee("Alice", 25, new MyDate(1998, 5, 12)));
// 添加其他Employee对象

// 按生日排序(定制排序)
TreeSet<Employee> set2 = new TreeSet<>(new Comparator<Employee>() {
    @Override
    public int compare(Employee o1, Employee o2) {
        // 实现日期比较逻辑
    }
});

实验结果

  • 成功创建Employee对象并存入TreeSet
  • 实现按姓名和生日的两种排序方式
  • 正确遍历输出排序结果

实验2:自定义泛型类的使用

实验目标

  • 掌握自定义泛型类的定义和使用
  • 理解泛型在数据访问层(DAO)中的应用
  • 使用JUnit进行单元测试

实验步骤

  1. 定义泛型DAO类
public class DAO<T> {
    private Map<String, T> map = new HashMap<>();

    public void save(String id, T entity) {
        map.put(id, entity);
    }

    public T get(String id) {
        return map.get(id);
    }

    public void update(String id, T entity) {
        map.put(id, entity);
    }

    public List<T> list() {
        return new ArrayList<>(map.values());
    }

    public void delete(String id) {
        map.remove(id);
    }
}
  1. 定义User类
public class User {
    private int id;
    private int age;
    private String name;

    // 构造器、getter、setter省略
}
  1. 编写测试类
public class DAOTest {
    @Test
    public void testDAO() {
        DAO<User> dao = new DAO<>();
        
        User u1 = new User(1, 25, "Alice");
        dao.save("001", u1);
        
        assertEquals(u1, dao.get("001"));
        
        User u2 = new User(2, 30, "Bob");
        dao.update("001", u2);
        
        assertEquals(1, dao.list().size());
        
        dao.delete("001");
        assertNull(dao.get("001"));
    }
}

实验结果

  • 成功实现泛型DAO类
  • 完成对User对象的CRUD操作
  • 通过JUnit测试验证功能正确性

实验总结

关键知识点

  1. 泛型集合

    • 类型安全
    • 自动类型转换
    • 减少代码重复
  2. 排序实现

    • Comparable接口:自然排序
    • Comparator接口:定制排序
  3. 泛型类设计

    • 类型参数化
    • 提高代码复用性
    • 增强类型安全性
  4. 单元测试

    • 使用JUnit验证功能
    • 保证代码质量
    • 支持重构

常见问题

  1. 类型擦除的影响

    • 运行时无法获取泛型类型信息
    • 解决方案:通过Class对象传递类型信息
  2. 泛型数组创建

    • 不能直接创建泛型数组
    • 解决方案:使用Object数组转换
  3. 通配符使用

    • 上界通配符:<? extends T>
    • 下界通配符:<? super T>
    • 无界通配符:<?>
泛型基础
集合框架
自定义泛型类
泛型方法
通配符
类型擦除
项目实践

Java泛型与集合练习题

泛型类设计

题目1:开发泛型Apple类

要求:

  • 设计一个泛型Apple类,包含重量属性weight
  • 实例化三个对象:
    • a1:weight为String类型,值为"500克"
    • a2:weight为Integer类型,值为500
    • a3:weight为Double类型,值为500.0
  • 输出各对象的weight值
  • 思考:为什么a2和a3需要使用包装类Integer和Double?

实现:

public class Apple<T> {
    private T weight;

    public Apple(T weight) {
        this.weight = weight;
    }

    public T getWeight() {
        return weight;
    }

    public static void main(String[] args) {
        Apple<String> a1 = new Apple<>("500克");
        Apple<Integer> a2 = new Apple<>(500);
        Apple<Double> a3 = new Apple<>(500.0);

        System.out.println("a1 weight: " + a1.getWeight());
        System.out.println("a2 weight: " + a2.getWeight());
        System.out.println("a3 weight: " + a3.getWeight());
    }
}

思考:

  • 泛型类型参数必须是引用类型,不能是基本类型(如int、double)

  • 因此需要使用包装类Integer和Double

集合操作

题目2:新闻类与ArrayList集合

要求:

  • 封装新闻类News,包含标题、作者、内容、类型属性

  • 重写toString方法,输出格式为"标题;类型;作者"

  • 重写equals和hashCode方法,标题相同即为同一条新闻

  • 创建ArrayList集合,添加三条新闻

  • 遍历集合,打印新闻标题,截取标题到10个汉字长度

实现:

public class News {
    private String title;
    private String author;
    private String content;
    private String type;

    // 构造器、getter、setter省略

    @Override
    public String toString() {
        return title + ";" + type + ";" + author;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        News news = (News) o;
        return title.equals(news.title);
    }

    @Override
    public int hashCode() {
        return title.hashCode();
    }
}

public class NewsTest {
    public static void main(String[] args) {
        List<News> newsList = new ArrayList<>();
        newsList.add(new News("标题1", "作者1", "内容1", "类型1"));
        newsList.add(new News("标题2", "作者2", "内容2", "类型2"));
        newsList.add(new News("标题3", "作者3", "内容3", "类型3"));

        for (News news : newsList) {
            String title = news.getTitle();
            System.out.println(title.length() > 10 ? title.substring(0, 10) : title);
        }
    }
}

Map集合操作

题目3:HashMap操作

要求:

  1. 使用HashMap存储员工姓名和工资

  2. 更新张三的工资为2600元

  3. 为所有员工加薪100元

  4. 遍历所有员工

  5. 遍历所有工资

实现:

public class EmployeeSalary {
    public static void main(String[] args) {
        Map<String, Integer> salaryMap = new HashMap<>();
        salaryMap.put("张三", 800);
        salaryMap.put("李四", 1500);
        salaryMap.put("王五", 3000);

        // 更新张三工资
        salaryMap.put("张三", 2600);

        // 所有员工加薪100元
        salaryMap.replaceAll((k, v) -> v + 100);

        // 遍历所有员工
        System.out.println("员工列表:");
        for (String name : salaryMap.keySet()) {
            System.out.println(name);
        }

        // 遍历所有工资
        System.out.println("工资列表:");
        for (Integer salary : salaryMap.values()) {
            System.out.println(salary);
        }
    }
}

相关文章:

  • Docker 从入门到进阶 (Win 环境) + Docker 常用命令
  • 【Android安卓移动计算】实现项目一[模拟器启动项目]
  • CSRF漏洞利用的小点总结
  • JVM 参数调优指南_优化 Java 应用性能
  • nginx中的limit_req 和 limit_conn
  • WPS宏开发手册——常见问题
  • MySQL基础 [三] - 数据类型
  • Java 大视界 -- Java 大数据在航天遥测数据分析中的技术突破与应用(177)
  • 记录clickhouse记录一次性能优化,从60s到1s
  • JavaScript创建对象与构造函数
  • TPM/HSM/TEE差异分析
  • 浏览器 路由详解
  • c++最小二乘法
  • Lucene.Net 分词器选择指南:盘古分词 vs 结巴分词
  • Spring 执行流程(源码)
  • dmsetup 清理ceph osd残留磁盘分区
  • 项目总结之常问的一些问题
  • Day16——路由2
  • 深入探究C++ 运算符重载:以日期类为例
  • 如何使用 DrissionPage 进行网页自动化和爬取
  • 网站免费建站 图标/山西seo关键词优化软件搜索
  • 什么网站可以做2.5D场景/企业网站快速建站
  • 网站seo工作内容/搜索引擎优化的主要特征
  • 网站的后期运营及维护费用/网络营销的成功案例有哪些
  • 政府网站集约化建设推进会/房产网站模板
  • 成都专业做网站的公司/seo门户网