集合心得总结
为什么需要集合? 数组有不足:扩容麻烦 保存元素类型固定,同一数组无法改变 ------> 引出集合的概念
集合体系
集合分为两大类 一类是Collection(一组对象) 一类是Map(一组键值对)
底层维护实现关系
ArrayList/Vector实现Collection的List接口 底层是可变类型数组Object[]
LinkedList实现Collection的List接口 底层是双向链表
TreeSet实现了Collection的Set接口 底层是TreeMap
HashSet实现了Collection的Set接口 底层是HashMap
LinkedHashSet实现了Map接口 底层是LinkedHashMap
Hashtable实现了Map接口 底层Entry数组
HashMap实现了Map接口 底层是Node类型数组table 在JDK7中 实现形式是数组+链表 在JDK8以后 实现形式是数组+链表+红黑树
LinkedHashMap实现了Map接口 底层是数组+双向链表
选择
1.判断存储的类型
一组对象/一组键值对
2.一组对象【单列】
Collection接口
允许重复:List
增删多:LinkedList【双向链表】
改查多:ArrayList/Vector【可变类型Object数组】
不允许重复:Set 底层都是Map接口的实现类 而Map的Key不允许重复
无序:HashSet
排序:TreeSet
插入和取出顺序一致: LinkedHashSet
3.一组键值对
Map接口 Key不允许重复 可以为空 Values可以重复 可以为null(多个) 一对K-V ---> 放在HashMap$Node Node实现了Entry接口 可以说一对K-V是一个Entry
1. k-v 最后是HashMap$Node
2. k-v为了方便程序员的遍历 还会创建 EntrySet 集合 该集合存放的元素的类型 Entry
而一个Entry对象 就有k,v EntrySet<Entry<k,v>> 即 transicent Set<Map.Entry<k,v>> entrySet;
3. entrySet 中 定义的类型是 Map.Entry 但是实际上存放的还是 HashMap$Node
这是因为 static class Node<K,V> implements Map.Entry<K,V>
4. 当把HashMap$Node 对象 存放到 entrySet 就方便我们的遍历
K getKey(); V getValue();
键无序:HashMap
键排序:TreeMap
键插入和取出一致:LinkedHashMap
读取文件:Properties
剖析类
Vector 安全 效率低---ArrayList 不安全 效率高 !!!扩容机制
ArrayList扩容完整过程
TreeSet--TreeMap 强调定制排序(匿名内部类)
HashSet--HashMap线程不安全 !!!扩容机制
重写hashCode equals 来控制当什么条件相同时返回相同的hashCode 就不能添加元素
LinkedHashSet
加入顺序和取出元素/数据的顺序一致
LinkedHashSet 底层维护的是一个LinkedHashMap(是HashMap的子类)
LinkedHashSet 底层结构 (数组 + 双向链表)
添加第一次时 直接将 数组table 扩容到16 存放的结点类型 LinkedHashMap\(Entry
数组是 HashMap\)Node[] 存放的元素/数据是 dHashMap$EntrLinkey类型
继承关系是在内部类完成
static class Entry<K,V> extends HashMap.Node<K,V> {
Entry<K,V> before, after;
Entry(int hash, K key, V value, Node<K,V> next) {
super(hash, key, value, next);
}
}
HashTable
Hashtable底层
底层有一个数组 Hashtable$Entry[] 初始化大小为11 临界值 threshold 8 = 11 * 0.75 扩容 按照自己的扩容机制来进行即可 执行 方法 addEntry(hash, key, value, index); 添加 k-v 装到Entry 当 if (count >= threshold) 满足时 进行扩容 按照 int newCapacity = (oldCapacity << 1) + 1;
方法
Collection:
Collections工具类方法
List/Set方法
add
remove
contains
size
isEmpty
addAll
containsAll
removeAll
get List接口有 Set接口无
Map:
put
remove
get
size
isEmpty
clear
containsKey
遍历
Collection:
Iterator
增强for
普通for
Set接口实现类没有get方法 不能通过普通for遍历
Map
Collections类
集合关系密切-----> 泛型配合使用
传统方法 不能对加入集合的元素进行约束 可能存在安全隐患
遍历的时候 需要类型转换 当数据量比较大的时候 会影响效率
![image](https://img2020.cnblogs.com/blog/2679279/202112/2679279-20211228152459833-2069123739.png)
经典题目
点击查看代码
public class Main {
public static void main(String[] args) {
ArrayList<Employee> employees = new ArrayList<>();
Employee em1 = new Employee("zhc", 20000, new MyDate(2002, 7, 9));
Employee em2 = new Employee("myh", 10000, new MyDate(2000, 4, 2));
Employee em3 = new Employee("zhc", 5000, new MyDate(2001, 2, 5));
employees.add(em2);
employees.add(em1);
employees.add(em3);
System.out.println("排序前");
System.out.println(employees);
employees.sort(new Comparator<Employee>() {
@Override
public int compare(Employee emp1, Employee emp2) {
//先按照 name 排序,如果 name 相同,则按生日日期的先后排序。【即:定制排序】
//先对传入的参数进行验证
if(!(emp1 instanceof Employee && emp2 instanceof Employee)) {
System.out.println("类型不正确..");
return 0;
}
//比较 name
int i = emp1.getName().compareTo(emp2.getName());
if(i != 0) {
return i;
}
return emp1.getBirthday().compareTo(emp2.getBirthday());//动态绑定到MyDate类的compareto方法
}
});
System.out.println("排序后");
System.out.println(employees);
}
}
@SuppressWarnings({"all"})
class Employee {
private String name;
private double sal;
private MyDate birthday;
public Employee(String name, double sal, MyDate birthday) {
this.name = name;
this.sal = sal;
this.birthday = birthday;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getSal() {
return sal;
}
public void setSal(double sal) {
this.sal = sal;
}
public MyDate getBirthday() {
return birthday;
}
public void setBirthday(MyDate birthday) {
this.birthday = birthday;
}
@Override
public String toString() {
return "\nEmployee{" +
"name='" + name + '\'' +
", sal=" + sal +
", birthday=" + birthday +
'}';
}
}
public class MyDate implements Comparable<MyDate> {
private int year;
private int month;
private int day;
public MyDate(int year, int month, int day) {
this.year = year;
this.month = month;
this.day = day;
}
public int getMonth() {
return month;
}
public void setMonth(int month) {
this.month = month;
}
public int getDay() {
return day;
}
public void setDay(int day) {
this.day = day;
}
public int getYear() {
return year;
}
public void setYear(int year) {
this.year = year;
}
@Override
public String toString() {
return "MyDate{" +
"month=" + month +
", day=" + day +
", year=" + year +
'}';
}
@Override
public int compareTo(MyDate o) { //把对 year - month - day
//如果year相同 就比较Month
int yearMinus = year - o.getYear();
if (yearMinus != 0) {
return yearMinus;
}
//如果year相同 就比较month
int monthMinus = month - o.getMonth();
if (monthMinus != 0) {
return monthMinus;
}
return day - o.getDay();
}
}
自定义泛型
自定义泛型类
·基本语法
class 类名 <T,R> {
成员
}
·细节
普通成员可以使用泛型 属性 方法
使用泛型的数组 不能初始化
静态方法中不能使用类的泛型
泛型类的类型 是在创建对象时确定的 因为创建对象时 需要指定类型
如果在创建对象时 没有指定类型 默认为Object
自定义泛型接口
·基本语法
interface 接口名 <T,R> {
}
·细节
接口中 成员是静态性质的 所以不能使用泛型
泛型接口的类型 在继承接口 或者 实现接口 是确定
没有指定类型 默认为Object
自定义泛型方法
·细节
泛型方法 可以定义在普通类 也可以定义在泛型类中
当泛型方法被调用时 类型会确定
public void eat(E e) 修饰符后没有 <T,R> eat 方法不是 泛型方法 而是使用了泛型