总结集合
集合:
Collection:
List:
ArrayList:
底层:自动扩容的数组。初始容量为10,扩容机制->int newCapacity = oldCapacity + (oldCapacity >> 1); 每次扩容原容量的1.5倍
优点:根据索引查找效率最高;元素有序可重复
缺点:增加删除效率较低,线程不安全
应用场景:查找多而增删少的场景
Vector:是List的实现类,它的继承链是java.util.AbstractCollection<E> -->java.util.AbstractList<E>-->java.util.Vector
底层:和ArrayList一样,可变长的数组,但扩容机制是每次扩容原容量的2倍
优点:根据索引查找效率最高;元素有序可重复,线程安全
缺点:效率比ArrayList低
LinkedList:
底层:双向链表(链表是一种数据结构,每个节点上除了存储数据还会存储下一个节点的地址,双向链表会存储前一个与下一个节点的地址)
可以操作首尾数据
优点:有序,可重复。增删效率比较快
缺点:按索引查找比较慢,线程不安全。
应用场景:大量增删操作,少量查找操作
ListIterator迭代器。List接口下的一种Iterato迭代器。作用是在使用迭代器一边遍历一边修改元素时,会造成ConcurrentModificationException异常。
而为此准备的ListIterator迭代器可以解决这个问题。它的add和remove方法可以一边遍历一边修改元素
Set:
HashSet:无序,不可重复
底层:基于HashMap的key。是一个数组+链表。该数组为一个节点数组,每个数组都存入一个链表头。这种数组元素存入链表头的结构被称为“桶”。
每次存入任意引用数据类型时,通常会执行下列操作:
1.先将存入的数据使用hashCode()方法计算该数据地址值获得int类型的hash值
2.将该获取的int值通过hash算法取得在数组上存入的位置,既桶位。该算法根据jdk版本各有不同。但目的都是让数据尽量散列(较为平均的存入节点数组)
3.将该数据存入所得的桶位中,若该位置上没有数据,则将该数据作为此处链表的链表头;若有数据,则遍历该链表,并使用equals方法比较,只存入不相等的。
注意:当链表长度>8且数组长度>64时,会将链表变为红黑树!
当用HashSet存储对象时,若想保证两个有相同成员变量值的对象不重复时,仅仅重写该对象的equals方法是不够的。因为确定桶位时,可能将同一属性值的两个变量分配到
不同的桶位里,此时他们无法被equals同时方法检测。所以我们要同时重写hashCode方法,让桶位根据对象的属性值来分配。这样相同属性值的对象会被分配在同一桶位中,
再由equals比较其内容。
规律:equals相同,hashCode一定相同;
hashCode相同,equals不一定相同
优点:同时具有链表和数组的特点,按照索引查找和增删效率都有提高(但任然比不上ArrayList的查和LinkedList的增删)
缺点:无序,线程不安全
应用场景:实现不存储相同数据,查询,增删效率较高的时候建议使用HashSet
TreeSet:无序,不可重复,但由于底层是平衡二叉树,所以存入的数据会自动升序。
底层:平衡二叉树(由TreeMap支持)
注意:当存储引用类型数据时,由于TreeSet的自动排序,会导致类型不匹配异常。此时,若我们想要去除异常并完成去重则需要自己定义比较规则。
此时,借助Comparable接口和Compartor接口来实现
比较器:
内部比较器|自然排序:Comparable。
默认存在,定义在类的内部,固定是硬编码的习惯
使用方法:相应的实体类实现Comparable接口,并重写CompareTo方法。根据方法返回值来决定排序方式:大于0则正序排,等于0则去重,小于0则倒序排
例子:
package com.mzq.practice;
import java.io.Serializable;
import java.util.Objects;
public class Students implements Comparable<Students>, Serializable {
private static final long serialVersionUID = -7424681982164699241L;
private int age;
private String name;
public Students() {
super();
}
public Students(int age, String name) {
super();
this.age = age;
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Students students = (Students) o;
return age == students.age &&
Objects.equals(name, students.name);
}
@Override
public int hashCode() {
return Objects.hash(age, name);
}
@Override
public int compareTo(Students o) {
return this.getAge()-o.getAge();
}
@Override
public String toString() {
return "Students{" +
"age=" + age +
", name='" + name + '\'' +
'}';
}
}
外部比较器|定制排序:定义在一个类型的外部的比较方式:实现Comparator<T>接口,重写compare(T t1, T t2)方法。由于该接口只有一个抽象方法,且只用于比较,功能比较单一
所以,一般使用匿名内部类或Lambda表达式来实现,不另外建类。
例子:
TreeSet <Students>set=new TreeSet<Students>();
TreeSet <Students>set=new TreeSet<Students>((p1,p2) ->p1.getAge()-p2.getAge());
Comparator <Students>co=new Comparator<Students>() {
@Override
public int compare(Students o1, Students o2) {
return o1.getAge()-o2.getAge();
}
};
Map:
HashMap<k,v>:底层:数组+链表+红黑树
k(键): 可以为任意引用数据类型数据 相当于 Set 无序不可重复
v(值): 可以为任意引用数据类型数据 相当于 Collection 可重复无序
一个key只能对应一个value->映射关系,可以根据key操作value
常用于存具有映射关系的两个值
扩容以及存储相关的概念:
1.该数据结构为节点数组+链表
2.每个节点上存hash值,key值,value值,下一个节点的地址
3.key值通过hashCode方法的到整数的hash值;该hash值再根据某种hash算法确定桶位索引。此过程会尽量保证散列。
4.判断该桶位上是否有节点,若有,则将该数据加在该节点所在的链表的最后;若无,则将该数据作为头结点放入该桶位。
5.jdk1.8后,当数组长度超过64,且链表长度都大于8,就会将链表结构换成红黑树
6.每个HashMap的默认容量是16;当容量超过扩容临界值是,会扩为原来的2倍
7.扩容临界值只与加载因子有关,默认的加载因子是0.75,而临界值=当前容量*加载因子
8.扩容最大不超过2^30;
遍历的三方法:使用keySet();value;Map.Entry遍历,例子如下:
HashMap<Teacher,String>map=new HashMap<Teacher,String>();
Teacher t1=new Teacher("qzm","java");
Teacher t2=new Teacher("mzq","bigdata");
Teacher t3=new Teacher("mzq","bigdata");
map.put(t1,t1.getLecture());
map.put(t2,t2.getLecture());
map.put(t3,t3.getLecture());
Set<String> names = map.keySet();
for (String s : names) {
System.out.println(s + "--->" + map.get(s));
}
Collection<String> values=map.values()
for (String s : names) {
System.out.println(s));
}
Set<Map .Entry<Teacher,String>> set=map.entrySet();
Iterator<Map.Entry<Teacher,String>> it=set.iterator();
while (it.hasNext()){
Map.Entry<Teacher,String> en= it.next();
System.out.println("教师姓名:"+en.getKey().getName()+";教授课程:"+en.getValue());
}
TreeMap<k,v>:
底层:红黑树,所以key会自动升序
注意:当用key存储引用类型数据时,也要自定义比较器,方法同TreeSet
遍历方式:参考HashMap
Properties:
作用:通常不作为容器,而作为读取配置文件用;这样我们可以实现软编码,增强灵活性。
使用步骤:
1.定义一个配置文件 xx.properties (键值对都是字符串)
2.创建Properties类型的对象,调用load从流中加载(输入流的数据源就是配置文件)
3.从配置文件中读取数据,加载使用
例子:
Properties pro = new Properties();
pro.load(Thread.currentThread().getContextClassLoader().getResourceAsStream("db.properties"));
System.out.println(pro.getProperty("username"));
System.out.println(pro.getProperty("password"));