一.集合体系图
1.Collection常用方法
public class CollectionTest {
@SuppressWarnings("all")
public static void main(String[] args) {
List list = new ArrayList<>();
//add:添加单个元素
list.add("jack");
list.add(10);
list.add(true);
System.out.println("list=" + list);
//Integer a = 10;
//remove:删除指定元素
//list.remove(0);
//list.remove(a);
System.out.println("list=" + list);
//contains:查找元素是否存在
System.out.println(list.contains(10));//返回布尔值
//size:获取元素个数
System.out.println(list.size());
//isEmpty:判断集合是否为空
System.out.println(list.isEmpty());
//clear:清空
//list.clear();
System.out.println("list="+ list);
ArrayList<String> list2 = new ArrayList<>();
list2.add("三国演义");
list2.add("红楼梦");
//addAll:添加多个元素
list.addAll(list2);
System.out.println("list="+ list);
//containsAll:查找多个元素是否都存在
System.out.println(list.containsAll(list2));
//removeAll:删除多个元素
list.removeAll(list2);
System.out.println("list="+ list);
}
}
2.迭代器与增强for的使用方式
迭代器
public class CollectionIterator {
public static void main(String[] args) {
Collection<Book> col = new ArrayList<>();
col.add(new Book("三国演义","罗贯中",10.1));
col.add(new Book("小李飞刀","古龙",5.1));
col.add(new Book("红楼梦","曹雪芹",34.6));
//1.先得到col对应的迭代器
Iterator<Book> iterator = col.iterator();
//2.使用while循环遍历,快捷键魏itit,ctul+j,显示所有快捷菜单
while (iterator.hasNext()){//判断是否还有数据
//返回下一个元素,默认返回类型是Object
Book book = iterator.next();
System.out.println("book="+ book);
}
//3.当推出while循环后,这时迭代器指向最后的元素
//4.如果希望再次遍历,需要重置迭代器
//iterator = col.iterator();
}
}
class Book{
private String name;
private String author;
private double price;
public Book(String name, String author, double price) {
this.name = name;
this.author = author;
this.price = price;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
@Override
public String toString() {
return "Book{" +
"name='" + name + '\'' +
", author='" + author + '\'' +
", price=" + price +
'}';
}
}
增强for
public class CollectionFor {
public static void main(String[] args) {
Collection<Book> col = new ArrayList<>();
col.add(new Book("三国演义","罗贯中",10.1));
col.add(new Book("小李飞刀","古龙",5.1));
col.add(new Book("红楼梦","曹雪芹",34.6));
//1.使用增强for,在Collection集合,也可以在数组使用
//2.增强for底层是迭代器
for (Object book : col){
System.out.println("book="+ book);
}
}
}
3.List接口基本介绍
(1)List接口是Collection接口的子接口
(2)List集合类中元素有序(即添加顺序和取出顺序一致)、且可重复
(3)List集合中的每个元素都对应一个整数型的序号记载其在容器中的位置,可以根据序号存取容器中的元素。
public class List_test {
public static void main(String[] args) {
List list = new ArrayList();
list.add("jack");
list.add("tom");
list.add("tom");
list.add("hsp");
System.out.println(list);
System.out.println(list.get(2));
}
}
List接口基本方法
public class ListMethod {
@SuppressWarnings("all")
public static void main(String[] args) {
List list = new ArrayList<>();
list.add("南哥");
list.add("国哥");
list.add("国哥");
list.add("国哥");
//void add(int index, Object ele):在index位置插入ele元素
list.add(1,"老韩");
//boolean addAll(int index, Collection eles):从index位置开始将eles中的所有元素插入进来
List list2 = new ArrayList<>();
list2.add("南哥1");
list2.add("国哥1");
list.addAll(1,list2);
System.out.println(list);
//Object get(int index):获取指定index位置的元素
System.out.println(list.get(1));
//int indexOf(Object obj):返回obj在集合中首次出现的位置
System.out.println(list.indexOf("国哥"));
//int lastIndexOf(Object obj):返回obj在当前集合中末次出现的位置
System.out.println(list.lastIndexOf("国哥"));
//Object remove(int index):移除指定index位置的元素,并返回此元素
System.out.println(list.remove(0));
System.out.println(list);
//Object set(int index, Object ele):设置指定index位置的元素ele,相当于是替换。
list.set(0,"整的");
System.out.println(list);
//List subList(int fromIndex, int toIndex):返回从fromIndex到toIndex位置的子集合
//不包含toIndex
System.out.println(list.subList(0,2));
}
}
ArrayList的注意事项及底层原理
(1)Arraylist可以添加一个或多个null。
(2)Arraylist是由数组来实现数据存储的。
(3)Arraylist基本等同于Vector,除了Arraylist是线程不安全的。
(4)ArrayList中维护了一个Object类型的数组elementData,transient Object[] elementData;
(5)当创建对象时,如果使用的是无参构造器,则初始elementData容量为0(jdk7是10)
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
------------------------------------------------------------------------------------------------------------
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
(6)当添加元素时,先判断是否需要扩容,如果需要扩容,则调用grow方法,否则直接添加元素到合适位置
(7)如果使用的是无参构造,如果第一次添加,需要扩容的话,则扩容elementData为10,如果需要再次扩容的话,则扩容 elementData为1.5倍。
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
//进行添加元素
elementData[size++] = e;
return true;
}
------------------------------------------------------------------------------------------------------------
private void ensureCapacityInternal(int minCapacity) {//minCapacity此时为1
//判断elementData是否为空数组
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
//private static final int DEFAULT_CAPACITY = 10;
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);//取一个最大值赋给minCapacity
}
//再次确认扩容容量
ensureExplicitCapacity(minCapacity);
}
------------------------------------------------------------------------------------------------------------
private void ensureExplicitCapacity(int minCapacity) {
modCount++;//记录当前集合被修改的次数
//再次确认:当前需要最小容量减去数组当前容量,如果大于零则需扩容
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
------------------------------------------------------------------------------------------------------------
//真正的扩容方法
private void grow(int minCapacity) {
// elementData.length目前还是0
int oldCapacity = elementData.length;
//相当于oldCapacity*1.5
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity < 0)
//把minCapacity(为10)赋给newCapacity扩容,所以第一次扩容为10
newCapacity = minCapacity;
//下面if先跳过
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// Arrays.copyOf这里真正进行扩容,这个方法底层还是用System.arraycopy方法
elementData = Arrays.copyOf(elementData, newCapacity);
}
(8)如果使用的是指定容量capacity的构造器,则初始elementData容量为capacity
(9)如果使用的是指定容量capacity的构造器,如果需要扩容,则直接扩容elementData为1.5倍
public ArrayList(int initialCapacity) {
if (initialCapacity > 0) {
this.elementData = new Object[initialCapacity];
} else if (initialCapacity == 0) {
this.elementData = EMPTY_ELEMENTDATA;
} else {
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
}
}
ArrayList与Vector对比
Vector基本原理
(1)当创建对象时,如果使用的是无参构造器,则初始elementData容量为10
public Vector() {
this(10);
}
(2)Vector直接以两倍扩容
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
//Vector直接以两倍扩容
int newCapacity = oldCapacity + ((capacityIncrement > 0) ?
capacityIncrement : oldCapacity);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
elementData = Arrays.copyOf(elementData, newCapacity);
}
LinkedList底层结构
(1)LinkedList底层实现了双向链表和双端队列
一、LinkedList维护的双向链表有两个属性,first和last分别指向首、尾节点
二、每个节点(Node对象),里面又维护了prev、next、item三个属性,其中通过prev指向前一个,next指向后一个。最终实现双向链表
三、所以LinkedList元素的添加和删除,不是通过数组完成的,相对来说效率比较高
(2)可以添加任意元素(元素可以重复),包括null,默认尾插
(3)线程不安全,没有实现同步
LinkedList.add方法
public boolean add(E e) {
linkLast(e);
return true;
}
------------------------------------------------------------------------------------------------------------
void linkLast(E e) {//e:1
final Node<E> l = last;//lastc第一次添加时为空,这句重点在后续添加
final Node<E> newNode = new Node<>(l, e, null);//创建一个新节点存储e,如果前面有节点则将prev指向前一个
last = newNode;//将last指向newNode
if (l == null)
first = newNode;//将first指向newNode
else
l.next = newNode;//这句重点在后续添加
size++;//记录集合大小,使用get方法时用来循环遍历集合
modCount++;//记录修改次数
}
LinkedList.size方法
public int size() {
return size;
}
LinkedList.get方法
public E get(int index) {
checkElementIndex(index);//判断index是否越界
return node(index).item;//返回index对应item
}
private void checkElementIndex(int index) {
if (!isElementIndex(index))
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
private boolean isElementIndex(int index) {
return index >= 0 && index < size;
}
------------------------------------------------------------------------------------------------------------
Node<E> node(int index) {
// assert isElementIndex(index);
//这个看不懂
if (index < (size >> 1)) {
Node<E> x = first;
for (int i = 0; i < index; i++)
x = x.next;
return x;
} else {
//通过下面循环从尾部遍历拿到index对应元素并返回
Node<E> x = last;
for (int i = size - 1; i > index; i--)
x = x.prev;
return x;
}
}
linkedList.remove()方法
无参构造默认删除头节点,也可以输入下标或值来删除对应节点
public E remove() {
return removeFirst();
}
//将f指向头节点,并判断是否为空,在调用unlinkFirst把f传进去
public E removeFirst() {
final Node<E> f = first;
if (f == null)
throw new NoSuchElementException();
return unlinkFirst(f);
}
private E unlinkFirst(Node<E> f) {
//拿到f对应element,next,再将原本头节点清空,以便回收
final E element = f.item;
final Node<E> next = f.next;
f.item = null;
f.next = null; // help GC
first = next;//将first指向原本头节点的下一个,使之成为头节点
if (next == null)//判断next是否为空,如果为空则清空集合
last = null;
else
next.prev = null;//将next的prev指向清空
size--;
modCount++;
return element;
}
ArrayList与linkedList对比
4.List接口实现子类的三种遍历方式
Iterator<Object> iterator = linkedList.iterator();
while (iterator.hasNext()){
Object next = iterator.next();
System.out.println(next);
}
for(Object list : linkedList){
System.out.println(list);
}
for (int i = 0; i < linkedList.size(); i++) {
System.out.println(linkedList.get(i));
}
5.set接口基本介绍
(1)无序(添加和取出的顺序不一致),没有索引
(2)不允许重复元素,所以只能有一个null
(3)遍历方式不能使用fori,可以用迭代器与增强for
public class HashSet01 {
public static void main(String[] args) {
Set set = new HashSet<>();
set.add("lucy");//添加成功
set.add("lucy");//添加失败
set.add(new String("lucy"));//添加失败
set.add(new Cat("tom"));//添加成功
set.add(new Cat("tom"));//添加成功
set.add(new String("h"));//添加成功
set.add(new String("h"));//添加失败
System.out.println(set);
}
}
class Cat{
private String name;
public Cat(String name) {
this.name = name;
}
@Override
public String toString() {
return "Cat{" +
"name='" + name + '\'' +
'}';
}
}
HashSet存储结构简单模拟
public class HashSetStructure {
public static void main(String[] args) {
//简单模拟一个HashSet的底层(就是HashMap的底层结构)
//数组+链表,这是不完整的
Node[] table = new Node[16];
Node john = new Node("john", null);
table[2] = john;
Node rose = new Node("Rose", null);
john.next = rose;
Node jack = new Node("jack", null);
rose.next = jack;
Node lucy = new Node("lucy", null);
table[3] = lucy;
System.out.println(table);
}
}
class Node{ //节点,存放数据,可以指向下一个节点,从而形成链表
Object item; //存放数据
Node next; //指向下一个节点
public Node(Object item, Node next) {
this.item = item;
this.next = next;
}
}
HashSet底层机制说明
(1)HashSet底层是HashMap,第一次添加时,table数组扩容到16,临界值为table.size*0.75,数组使用大于临界值就会进行扩容
(2)添加一个元素时,先得到hash值,会转成索引值
(3)找到存储数据表table,看这个索引位置是否已经存放有元素,如果没有,直接加入;如果有,调用equals比较,如果相同,就放弃添加,如果不相同,则添加到最后。(equals比较什么内容可以由程序员来定,可以重写equals)
(4)在Java8中,如果一条链表的元素个数>=TREEIFY_THRESHOLD(默认是8),并且table的大小 >= MIN_TREEIFY_CAPACITY(默认64),就会进行树化(红黑树)否则会继续扩容知道满足条件再树化,注意:在把元素添加到链表后,立即判断该链表是否已经>=8个节点
(5)table数组长度最大应该为0~99
public HashSet() {
map = new HashMap<>();
}
------------------------------------------------------------------------------------------------------------
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
------------------------------------------------------------------------------------------------------------
//执行put方法会执行hash(key),得到key对应的hash值(不等于hashCode,因为做了运算)算法(h = key.hashCode()) ^ (h >>> 16)
public V put(K key, V value) {
return putVal(hash(key), key, value, false, true);
}
------------------------------------------------------------------------------------------------------------
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,boolean evict) {
Node<K,V>[] tab; Node<K,V> p; int n, i;//定义了辅助变量
//table就是HashMap的一个数组,类型是Node[]
//table初始为null,条件成立,if语句表示如果当前table == null,或者大小 == 0
//就是第一次扩容到16个空间
if ((tab = table) == null || (n = tab.length) == 0)
n = (tab = resize()).length;
//根据key,得到hash,去计算该key应该存放到table表的哪个索引位置
//tab[i = (n - 1) & hash]),n为当前数组大小,再与当前元素hash值进行&运算得到位置索引i
//并把这个位置的对象,赋给p,再判断p是否为null
//如果p为null,表示这个索引位置还没有存放元素,就创建一个Node存放进去
if ((p = tab[i = (n - 1) & hash]) == null)
tab[i] = newNode(hash, key, value, null);
else {
Node<K,V> e; K k;
//判断当前添加的元素与集合中对应索引的元素hash是否相同
//再判断key是否相同,这里同时用了==和equals进行比较,重点是理解这里。final K key;
if (p.hash == hash &&
((k = p.key) == key || (key != null && key.equals(k))))
e = p;
else if (p instanceof TreeNode)
e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
else {
//for:如果该索引位置已有元素,则进行循环比较此索引链表,再进行添加或不添加
for (int binCount = 0; ; ++binCount) {
if ((e = p.next) == null) {
p.next = newNode(hash, key, value, null);
//注意:在把元素添加到链表后,立即判断该链表是否已经>=8个节点
//trun:就调用treeifyBin(tab, hash)对当前这个链表进行树化(转红黑树)
//树化条件:table>=64,链表节点>=8,否则先进行扩容满足条件再树化
if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
treeifyBin(tab, hash);
break;
}
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
break;
p = e;
}
}
if (e != null) { // existing mapping for key
V oldValue = e.value;
if (!onlyIfAbsent || oldValue == null)
e.value = value;//如果重复添加就替换value值
afterNodeAccess(e);
return oldValue;
}
}
++modCount;//记录修改次数
//判断size是否大于临界值,大于就进行扩容
if (++size > threshold)
resize();
afterNodeInsertion(evict);//留给HashMap子类实现一些功能
return null;//返回null相当于添加成功了
}
------------------------------------------------------------------------------------------------------------
final Node<K,V>[] resize() {
Node<K,V>[] oldTab = table;
int oldCap = (oldTab == null) ? 0 : oldTab.length;
int oldThr = threshold;
int newCap, newThr = 0;
if (oldCap > 0) {
if (oldCap >= MAXIMUM_CAPACITY) {
threshold = Integer.MAX_VALUE;
return oldTab;
}
else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
oldCap >= DEFAULT_INITIAL_CAPACITY)
newThr = oldThr << 1; // double threshold
}
else if (oldThr > 0) // initial capacity was placed in threshold
newCap = oldThr;
else {//static final int DEFAULT_INITIAL_CAPACITY = 1 << 4;
newCap = DEFAULT_INITIAL_CAPACITY;//16
//static final float DEFAULT_LOAD_FACTOR = 0.75f
//这是一个临界值,大于这个值就会开始扩容
newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
}
if (newThr == 0) {
float ft = (float)newCap * loadFactor;
newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?
(int)ft : Integer.MAX_VALUE);
}
threshold = newThr;
@SuppressWarnings({"rawtypes","unchecked"})
Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];//创建数组进行
table = newTab;
if (oldTab != null) {
for (int j = 0; j < oldCap; ++j) {
Node<K,V> e;
if ((e = oldTab[j]) != null) {
oldTab[j] = null;
if (e.next == null)
newTab[e.hash & (newCap - 1)] = e;
else if (e instanceof TreeNode)
((TreeNode<K,V>)e).split(this, newTab, j, oldCap);
else { // preserve order
Node<K,V> loHead = null, loTail = null;
Node<K,V> hiHead = null, hiTail = null;
Node<K,V> next;
do {
next = e.next;
if ((e.hash & oldCap) == 0) {
if (loTail == null)
loHead = e;
else
loTail.next = e;
loTail = e;
}
else {
if (hiTail == null)
hiHead = e;
else
hiTail.next = e;
hiTail = e;
}
} while ((e = next) != null);
if (loTail != null) {
loTail.next = null;
newTab[j] = loHead;
}
if (hiTail != null) {
hiTail.next = null;
newTab[j + oldCap] = hiHead;
}
}
}
}
}
return newTab;
}
LinkedHashSet基本说明
(1)LinkedHashSet是HashSet的子类
(2)LinkedHashSet底层是一个LinkedHashMap,底层维护了一个数组+双向链表
(3)LinkedHashSet根据元素的hashCode值来决定元素的存储位置,同时使用链表维护元素的次序,这使得元素看以来是以插入顺序保存的
(4)LinkedHashSet不允许添加重复元素,第一次添加时,直接将数组table扩容到16,
(5)数组是HashMap N o d e [ ] , 存放的节点类型是 L i n k e d H a s h M a p Node [ ] ,存放的节点类型是LinkedHashMap Node[],存放的节点类型是LinkedHashMapEntry
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);
}
}
6.Map接口
Map接口常用方法
(1)put:添加
(2)remove:根据键删除映射关系
(3)get:根据键获取值
(4)size:获取元素个数
(5)isEmpty:判断个数是否为0
(6)clear:清除
(7)containsKey:查找键是否存在
Map接口实现类的特点
(1)Map用于保存具有映射关系的数据:Key-Value
(2)Map中的Key和Value可以是任何引用类型数据,会封装到HashMap$Node对象中
(3)Map中的Key不可以重复,Value可以重复,如果Key重复则会替换Value
if (!onlyIfAbsent || oldValue == null)
e.value = value;//如果重复添加就替换value值
(4)Map中的Key可以为null,value也可以为null,key为null,只能有一个,value可以有多个null
(5)常用String类作为Map的key,也可以使用Object或子类
(6)key和value之间存在单向一对一关系,即通过key总能找到对应的value
Map 一> EntrySet理解(重难点)
public class MapSource_ {
public static void main(String[] args) {
Map map = new HashMap<>();
map.put("no1","韩");
map.put("no2","张");
//1. k-v 最后是HashMap$Node node = newNode(hash, key, value, null)
//2. k-v 为了方便程序员的遍历,还会创建EctrySet集合,该集合存放的元素的类型是Entry,
//而一个Entry对象就有k,v EntrySet<Entry<k,v>> 即transient 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 就方便遍历,因为 Map.Entry 提供了重要方法
// K getKey(); V getValue();
Set set = map.entrySet();
System.out.println(set.getClass());//HashMap$EntrySet
for (Object o : set) {
//System.out.println(o.getClass());//HashMap$Node
Map.Entry entry = (Map.Entry) o;
System.out.println(entry.getKey() + "-" + entry.getValue());
}
}
}
Map六种遍历方式
public class mapFor {
public static void main(String[] args) {
Map map = new HashMap<>();
map.put("no1","韩");
map.put("no2","张");
Set keyset = map.keySet();
//1.增强for
for (Object o : keyset) {
System.out.println(o + "-" + map.get(o));
}
System.out.println("--------------------");
//2.迭代器
Iterator iterator = keyset.iterator();
while (iterator.hasNext()) {
Object next = iterator.next();
System.out.println(next + "-" + map.get(next));
}
System.out.println("--------------------");
//3.取出所有的Values
Collection values = map.values();
//这里可以使用所有Collection的遍历方法
//增强for
for (Object value : values) {
System.out.println(value);
}
System.out.println("------迭代器----------");
//迭代器
Iterator iterator1 = values.iterator();
while (iterator1.hasNext()) {
Object next = iterator1.next();
System.out.println(next);
}
System.out.println("--------entrySet--------");
//4.通过EctrySet来获取K-V
Set entrySet = map.entrySet();//EntrySet<Map.Entry<K,V>>
//(1)增强for
for (Object entry : entrySet) {
Map.Entry m = (Map.Entry) entry;
System.out.println(m.getKey() + "-" + m.getValue());
}
System.out.println("--------迭代器--------");
//迭代器
Iterator iterator2 = entrySet.iterator();
while (iterator2.hasNext()) {
Object next = iterator2.next();
Map.Entry m = (Map.Entry) next;
System.out.println(m.getKey() + "-" + m.getValue());
}
}
}
HashMap底层机制在HashSet已讲,这里不再赘述
HashTable基本介绍
(1)存放的元素是键值对:k-v
(2)hashtable的键和值都不能为null
(3)hashTable使用方法基本上与HashMap一样
(4)hashTable是线程安全的(synchronized),HashMap是线程不安全的
(5)底层数组Hashtable$Entry[ ] 初始化大小为11,临界值为11*0.75,扩容机制为size * 2 + 1
public synchronized V put(K key, V value) {
//执行以下代码如果value都为null,则抛异常
if (value == null) {
throw new NullPointerException();
}
// Makes sure the key is not already in the hashtable.
Entry<?,?> tab[] = table;
//执行以下代码如果key为null,则抛异常
int hash = key.hashCode();
//计算存储索引
int index = (hash & 0x7FFFFFFF) % tab.length;
Entry<K,V> entry = (Entry<K,V>)tab[index];
for(; entry != null ; entry = entry.next) {
if ((entry.hash == hash) && entry.key.equals(key)) {
V old = entry.value;
entry.value = value;
return old;
}
}
//添加方法
addEntry(hash, key, value, index);
return null;
}
private void addEntry(int hash, K key, V value, int index) {
modCount++;
Entry<?,?> tab[] = table;
if (count >= threshold) {
// 预扩容方法
rehash();
tab = table;
hash = key.hashCode();
index = (hash & 0x7FFFFFFF) % tab.length;
}
//创建元素并插入,使用头插
Entry<K,V> e = (Entry<K,V>) tab[index];
tab[index] = new Entry<>(hash, key, value, e);
count++;
}
总结-(开发中如何选择集合实现类)
在开发中,选择什么集合实现类,主要取决于业务操作特点,然后根据集合实现类特性进行选择。
(1)先判断存储的类型(一组对象[单列]或一组键值对[双列])
(2)一组对象[单列]:Collection接口
允许重复:List
增删多:LinkedList[底层维护了一个双向链表]
改查多:ArrayList[底层维护Object类型的可变数组]
不允许重复:Set
无序:HashSet[底层是HashMap,维护了一个哈希表,即(数组+链表+红黑树)]
排序:TreeSet
插入和取出顺序一致:LinkedHashSet,维护数组+双向链表
(3)一组键值对[双列]:Map
键无序:HashMap[底层是:哈希表: 数组+链表+红黑树]
键排序:TreeMap
键插入和取出顺序一致:LinkedHashMap
读取文件:Properties