招聘app保定网站seo
第十一章 Java集合框架
集合框架全景图
mindmaproot((Java集合))Collection单列List有序可重复ArrayListLinkedListVectorSet无序唯一HashSetLinkedHashSetTreeSetMap双列HashMapLinkedHashMapTreeMapHashtablePropertiesToolsCollectionsArrays
三大核心接口对比
特性 | List | Set | Map |
---|---|---|---|
元素顺序 | 插入顺序 | 无顺序 | Key唯一无序 |
元素重复性 | 允许重复 | 不允许重复 | Key唯一,Value可重复 |
常用实现类 | ArrayList/LinkedList | HashSet/TreeSet | HashMap/TreeMap |
线程安全实现 | CopyOnWriteArrayList | ConcurrentSkipListSet | ConcurrentHashMap |
ArrayList vs LinkedList
特性 | ArrayList | LinkedList |
---|---|---|
底层结构 | 动态数组 | 双向链表 |
随机访问速度 | O(1) | O(n) |
头尾操作效率 | 尾部快,头部慢 | 头尾都快 |
内存占用 | 连续空间,节省 | 节点存储,占用更多 |
扩容机制 | 1.5倍扩容 | 无扩容概念 |
HashMap深度解析
JDK1.8+ 存储结构
graph LRA[数组] --> 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锁 |
CopyOnWrite | CopyOnWriteArrayList | 写时复制,读无锁 |
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());
}
高频面试题精解
-
HashMap工作原理
- 通过hashcode计算存储位置
- JDK1.8采用数组+链表+红黑树
- 扩容时重新计算位置(2次幂扩展)
-
ConcurrentHashMap实现
- JDK1.7:分段锁(Segment)
- JDK1.8:Node+CAS+synchronized
-
ArrayList扩容机制
// 扩容核心代码(JDK1.8) int newCapacity = oldCapacity + (oldCapacity >> 1); // 1.5倍
-
Iterator快速失败机制
- modCount记录修改次数
- 遍历时检测modCount变化
📘 实战建议
- 根据场景选择集合:查询多用ArrayList,增删多用LinkedList
- 预估数据量时显式指定初始容量(特别对HashMap)
- 多线程环境优先使用JUC并发集合
- 复杂对象作为Map键时,必须正确重写hashCode和equals
- 遍历时优先使用增强for循环或迭代器,避免用fori遍历链表结构
第十二章 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();
泛型高级特性
泛型继承关系
通配符使用
// 上界通配符
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();
泛型限制与注意事项
使用限制
常见问题
- 类型擦除:泛型信息在编译后被擦除
// 编译前
List<String> list = new ArrayList<>();// 编译后(类型擦除)
List list = new ArrayList();
- 泛型数组:不能直接创建
// 错误示例
// List<String>[] array = new List<String>[10];// 正确方式
List<?>[] array = new List<?>[10];
- 泛型异常:不能捕获泛型异常
// 错误示例
// try { ... } catch (T e) { ... }
最佳实践建议
-
命名规范:使用单个大写字母作为类型参数
-
E:Element(集合元素)
-
K:Key(键)
-
V:Value(值)
-
T:Type(类型)
-
-
边界控制:合理使用上下界通配符
-
生产者使用
<? extends T>
-
消费者使用
<? super T>
-
-
类型安全:优先使用泛型集合
// 推荐
List<String> list = new ArrayList<>();// 不推荐
List list = new ArrayList();
- 代码复用:善用泛型方法
public static <T> T getFirst(List<T> list) {return list.get(0);
}
Java泛型实验
实验1:集合中使用泛型
实验目标
- 掌握在集合中使用泛型的方法
- 理解Comparable和Comparator接口的使用
- 实现自定义对象的排序
实验步骤
- 定义Employee类
public class Employee implements Comparable<Employee> {private String name;private int age;private MyDate birthday;// 构造器、getter、setter省略@Overridepublic int compareTo(Employee o) {return this.name.compareTo(o.name);}@Overridepublic String toString() {return "Employee{" +"name='" + name + '\'' +", age=" + age +", birthday=" + birthday +'}';}
}
- 定义MyDate类
public class MyDate {private int year;private int month;private int day;// 构造器、getter、setter省略
}
- 实现排序
// 按姓名排序(自然排序)
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>() {@Overridepublic int compare(Employee o1, Employee o2) {// 实现日期比较逻辑}
});
实验结果
- 成功创建Employee对象并存入TreeSet
- 实现按姓名和生日的两种排序方式
- 正确遍历输出排序结果
实验2:自定义泛型类的使用
实验目标
- 掌握自定义泛型类的定义和使用
- 理解泛型在数据访问层(DAO)中的应用
- 使用JUnit进行单元测试
实验步骤
- 定义泛型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);}
}
- 定义User类
public class User {private int id;private int age;private String name;// 构造器、getter、setter省略
}
- 编写测试类
public class DAOTest {@Testpublic 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测试验证功能正确性
实验总结
关键知识点
-
泛型集合
- 类型安全
- 自动类型转换
- 减少代码重复
-
排序实现
- Comparable接口:自然排序
- Comparator接口:定制排序
-
泛型类设计
- 类型参数化
- 提高代码复用性
- 增强类型安全性
-
单元测试
- 使用JUnit验证功能
- 保证代码质量
- 支持重构
常见问题
-
类型擦除的影响
- 运行时无法获取泛型类型信息
- 解决方案:通过Class对象传递类型信息
-
泛型数组创建
- 不能直接创建泛型数组
- 解决方案:使用Object数组转换
-
通配符使用
- 上界通配符:
<? 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省略@Overridepublic String toString() {return title + ";" + type + ";" + author;}@Overridepublic 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);}@Overridepublic 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操作
要求:
-
使用HashMap存储员工姓名和工资
-
更新张三的工资为2600元
-
为所有员工加薪100元
-
遍历所有员工
-
遍历所有工资
实现:
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);}}
}