集合Vector底层结构和ArrayList的比较 底层结构 版本 线程安全(同步)效率 扩容倍数 ArrayList可变数组jdk1.2不安全,效率高如果有参构造就是1.5倍扩容 如果是无参构造 1.第一次10 2.从第二次开
如果是无参构造
1.第一次10
2.从第二次开始按1.5倍扩 Vector 可变数组Object[] jdk1.0 安全,效率不高 如果是无参,默认10,满后,就按2倍扩容
如果指定大小,则每次直接按2倍扩 ArrayList和LinkedList的比较
数组扩容 较高 LinkedList 双向链表 较高
通过链表追加 较低
如何选择:
- 如果我们改查的操作多,选择ArrayList
- 如果我们增删的操作多,选择LinkedList
- 一般来说,再程序中,80%-90%都是查询,因此大部分情况会选择ArrayList
Set集合特点:
- 不包含重复元素的集合
- 没有带索引的方法,所以不能使用普通for循环遍历
- HashSet底层是HashMap,第一次添加时,table数组扩容到16,临界值(threshold)是16*加载因子(loadFator)是0.75 = 12;
- 如果table数组使用到了临界值12,就会扩容到16*2 =32,新的临界值就是32*0.75 = 24,依次类推;
- 在java8中,如果一条链表的元素个数到达TREEIFY_THRESHOLD(默认是8),并且table大小>=MIN_TREEIFY_CAPACITY(默认是64),就会进行树化(红黑树),否则仍然采用数组扩容机制;
注意:这里说的是jdk8
- Map用于保存具有映射关系的数据:Key-Value
- Map中的key和value可以是任何引用类型的数据,会封装到HashMap$Node对象中
- Map中的key不允许重复,原因和HashSet一样,如果有相同的key则新的value替换旧的value
- Map中的value可以重复
- Map的key可以为null,value也可以为null,注意key为null话只能有一个,value为null可以有多个;
- 常用String类作为Map的key
- key和value之间存在单向一对一关系,即通过指定的key总能找到对应的value
- Map接口的常用实现类:HashMap、Hashtable和Properties
- HashMap是Map接口使用频率最高的实现类
- HashMap是以key-val对应的方式来存储数据
- key值不能重复,但是value可以重复,允许使用null键和null值
- 如果添加相同的key,则会覆盖原来的key-val,等同于修改(key不会替换,val会替换)
- 与HashSet一样,不保证映射的顺序,因为磁层是以hash表的方式来存储的
- HashMap没有实现同步,因此是线程不安全的
- 存放的元素是键值对:k-v
- hashTable的键和值都不能为null,否则会抛出NullPointerException
- hashTable使用方法基本上和HashMap一样
- hashTable是线程安全的,hashMap是线程不安全的
- Properties类继承自Hashtable类并且实现了Map接口,也是使用一种键值对的形式来保存数据。
- 它的使用特点和Hashtable类似
- Properties还可以用于从xxx.properties文件中,加载数据到Properties类对象,并进行读取和修改
- key和value不能为null
在开发中,选择上面集合实现类,主要取决于业务操作特点,然后根绝结合实现类特性进行选择,分析如下:
1.先判断存储的类型(一组对象[单列]或是一组键值对[双列])
2.一组对象[单列]:Collection接口
允许重复:List
增删多:LinkedList[底层维护了一个双向链表]
改查多:ArrayList[底层维护Object类型的可变数组]
不允许重复:Set
无序:HashSet [底层是HashMap,维护了一个哈希表,即(数组+链表+红黑树)]
排序:TreeSet
插入和取出顺序一致:LinkedHashSet,维护的是数组+双向链表
3.一组键值对[双列]:Map
键无序:HashMap [底层是:哈希表 jdk7:数组+链表,jdk8:数组+链表+红黑树]
键排序:TreeMap
键插入和取出顺序一致:LinkedHashMap
读取文件:Properties
HashSet和TreeSet是如何实现去重的分析下面代码
- HashSet去重机制:hashCode() + equals(),底层先通过存入对象,进行运算得到hash值,通过hash值得到对应的索引,如果发现得到的索引对应table中相同索引的位置没有数据则直接存放,如果有数据,则会进行equals比较[遍历比较],如果比较后不相同则加入,否则不加入。
- TreeSet去重机制:如果你传入了一个Comparator匿名对象,就是用实现的compare去重,如果方法返回0,就认为是相同的元素/数据,就不添加,如果你没有传入一个Comparator匿名对象,则以你添加的对象实现的Comparable接口的compareTo去重。
package com.example.homework;
import java.util.HashSet;
import java.util.Objects;
/**
* @Author 郜庆辉
* @Time 2022/5/28 17:47
* @Version 1.0
*/
public class Homework4 {
public static void main(String[] args) {
HashSet set = new HashSet();
Person p1 = new Person(1001, "AA");
Person p2 = new Person(1002, "BB");
set.add(p1); //第一次添加的1001, "AA",p1的hash值为10
set.add(p2);
System.out.println(set);
p1.name = "CC"; //修改后的1001, "CC",p1的hash值还是10,只不过CC把AA占了
System.out.println(set);
set.remove(p1);//remove时也会计算hash值,但是计算的是1001, "CC"的hash值;hash值为11,删除的就hash值为11的索引位置;即p1没有删除成功,因为p1的hash为10;
System.out.println(set);
set.add(new Person(1001, "CC"));//这里添加的是按照1001, "CC"hash值进行添加的,同样会添加成功,hash值为12;
System.out.println(set);
set.add(new Person(1001,"AA")); //这里又添加的1001,"AA"hash值为10,跟p1的hash值一样,按理说不会添加成功,但是现在P1是1001,"CC",hash不一样时用equals比较,value不同,也可以添加成功。
System.out.println(set);
}
}
class Person {
public int id;
public String name;
public Person(int id, String name) {
this.id = id;
this.name = name;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
return id == person.id && Objects.equals(name, person.name);
}
@Override
public int hashCode() {
return Objects.hash(id, name);
}
// @Override
// public String toString() {
// return "Person{" +
// "id=" + id +
// ", name='" + name + '\'' +
// '}';
// }
}
Vector和ArrayList比较
1.第一次扩容10
2.从第二次开始按照1.5倍扩容 Vector 可变数组
Object[] jdk1.0 安全,效率不高 如果是无参,默认10,满后按照2倍扩容
如果是指定大小创建Vector,则每次按照2倍扩容。