集合
集合框架概述
集合和数组都是对多个数据进行存储操作的结构,也就是容器。此时存储的主要是内存层次的存储,也就是临时容器,而不是向数据库或者文件那种持久化的容器。而Java中的集合就是一种很好的临时容器,可以动态的把多个对象放入到容器中。
此前用到的数组也可以保存数据,但是数组的缺点也很多:
- 长度在开始时必须指定,一旦指定不能更改。
- 保存的数据必须为同一类型的元素。
- 使用数组进行添加、删除、插入等操作比较麻烦。
- 数组存储数据是有序、可重复的,不能存储无序不可重复的需求。
集合却没有这些问题:
- 可以动态保存任意多个对象,使用比较方便!
- 提供了一系列方便的操作对象的方法:add、remove、set、get等。
- 使用集合添加,删除新元素比较简单。
集合框架体系
Java集合的集合类很多,主要分为两大类(单列集合和双列集合):
4. Collection接口:单列集合,用来存储一个一个的对象。又分为List和Set。
5. Map接口:双列集合、用来存储一对一对的数据。
迭代器
基本介绍
- Iterator对象称为迭代器,主要用于遍历Collection集合中的的元素。
- 所有实现了Collection接口的集合类都有一个iterator()方法,用以返回一个实现了Iterator接口的对象,即可以返回一个迭代器。
- Iterator仅用于遍历集合,Iterator本身并不存放对象。
迭代器执行原理
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
/**
* @author Wen先森
* @version 1.0
* @date 2022/3/29 17:05
*/
public class Demo03 {
public static void main(String[] args) {
List list = new ArrayList();
list.add(1);
list.add(2);
list.add(3);//创建一个集合并添加数据
Iterator iterator =list.iterator();//得到一个集合的迭代器
while (iterator.hasNext()){//hasNext():判断是否还有下一个元素
System.out.println(iterator.next());//next():作用是下移并且将下移以后集合位置上的元素返回。
}
}
}
在调用iterator.next()方法之前必须要调用iterator.hasnext()检测判断是否还有下一个元素,如果不调用,且没有下一条元素,则调用iterator.next()会抛出异常。
Collection接口
- Collection实现子类可以存放多个元素,每个元素可以是Object。
- 有些Collection的实现类,可以存放重复的元素,有些不可以。
- 有些Collection的实现类,有些是有序的(List),有些是无序的(Set)。
- Collection接口没有直接的实现子类,是通过它的子接口Set和List来实现的。
Collection接口常用方法:
常用方法名 | 方法说明 |
---|---|
add(); | 添加单个元素 |
remove(); | 删除指定元素 |
contains(); | 查找元素是否存在 |
size(); | 获取元素个数 |
isEmpty(); | 判断是否为空 |
clear(); | 清空 |
addAll(); | 添加多个元素 |
removeAll(); | 删除多个元素 |
ContainsAll(); | 查找多个元素 |
List接口
List接口是Collection接口的子接口
- List集合类中的元素时有序的(即添加顺序和取出顺序是一致的)、且可重复。
- List集合中的每个元素都有其对应的顺序索引,即支持索引。
- List容器中的元素都对应一个整数型的序号记载其在容器中的位置,可以根据序号存取容器中的元素。
- List接口所有已知实现类:AbstractList, AbstractSequentialList, ArrayList, AttributeList, CopyOnWriteArrayList, LinkedList, RoleList, RoleUnresolvedList, Stack, Vector
- List接口常用实现类:ArrayList、LinkedList、Vector。
List接口常用方法:
常用方法名 | 方法说明 |
void add(int index,Object ele) | 在index位置插入ele元素 |
boolean addAll(int index,Collection eles) | 从index位置开始将eles集合中的所有元素添加进去 |
Object get(int index) | 获取指定index位置的元素 |
int indexOf(Object obj) | 返回obj元素在当前集合中首次出现的位置 |
int lastIndexOf(Object obj) | 返回obj元素在当前集合中最后一次出现的位置 |
Object remove(int index) | 移除指定index位置的元素,并返回此元素 |
Object set(int index,Object ele) | 设置指定index位置的元素为ele,相当于替换 |
List subList(int fromIndex,int toIndex) | 返回从fromIndex到toIndex(不包含toIndex)位置的子集合 |
List三种遍历方式
1.迭代器遍历 Iterator iterator=list.iterator(); while (iterator.hasNext()) { Object next = iterator.next(); System.out.println(next); }
2.增强for循环
for (Object obj :list) { System.out.println(obj); }
3.普通for循环
for (int i = 0; i < list.size(); i++) { Object obj=list.get(i); System.out.println(obj); }
ArrayList实现类底层结构
ArrayList注意事项:
- ArrayList可以加入null,并且多个也可以。
- ArrayList是由数组来实现数据存储的。
- ArrayList基本等同于Vector。除了ArrayList是线程不安全的,在多线情况下,不建议使用ArrayList。
ArrayList扩容机制:
- ArrayList中维护了一个Object类型的数组elementData。即:transient Object [] elementData ; //transient表示瞬间,短暂的,表示属性不会被序列化
- 当创建ArrayList对象时,如果使用的是无参构造器,则初始elementData容量为0,第一次添加,则扩容elementData为10,如果需要再次扩容,则扩容elementData为1.5倍。
- 如果使用的是指定大小的构造器,则初始elementData容量为指定大小,如果需要扩容,则扩容elementData为1.5倍。
分析使用无参构造器,创建和使用ArrayList的源码:
//创建一个空的elementData数组={}
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
//执行list.add
//先确认是否需要扩容,
//然后再执行赋值
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
//该方法确定minCapacity
//第一次扩容为10
private static int calculateCapacity(Object[] elementData, int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}
//mouCount++记录集合被修改的次数
//如果elementData的大小不够,则调用grow()扩容
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
//调用grow()扩容
//第一次扩容newCpacity=10
//第二次及以后,按照之前大小的1.5倍扩容
//扩容使用的是Arrays.copy()
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1); //oldCapacity >> 1相当于/2
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// minCapacity is usually close to size, so this is a win:
elementData = Arrays.copyOf(elementData, newCapacity);
}
使用有参构造器,创建使用ArrayList源码:
//创建一个指定大小的elementData数组
//大小大于0时, this.elementData = new Object[initialCapacity];
//等于0时,相当于无参构造器,创建一个空数组。
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);
}
}
如果是有参数的构造器,扩容机制:
- 第一次扩容,就按照elementData的1.5倍扩容。
- 整个执行流程还是和无参的一样。
Vector实现类底层结构
- Vector底层也是一个对象数组,protected Object[] elementData;
- Vector是线程同步的,即线程安全,Vector类的操作方法带有synchronized同步锁。
- 在开发中,需要线程同步安全时,考虑使用Vector。
ArrayList和Vector的比较
实现类 | 底层结构 | 版本 | 线程安全(同步)效率 | 扩容倍数 |
---|---|---|---|---|
ArrayList | 可变数组 | jdk1.2 | 不安全,效率高 | 1.如果有参构造扩容按照1.5倍 2.如果是无参,第一次10,第二次开始同样按照1.5倍扩容 |
Vector | 可变数组 | Jdk1.0 | 安全,效率不高 | 1.有参指定大小,则每次扩容按照2倍 2.如果是无参,第一次10,第二次开始同样按照2倍扩容 |
LinkedList底层结构
- LinkedList底层实现了双向链表和双端队列特点。
- 可以添加任何元素(元素可以重复),包括null。
- 线程不安全,没有实现同步。
- LinkedList中维护了两个属性first和last分别指向首节点和尾节点。
- 每个节点(Node对象),里面有维护了pre,next,item三个属性,其中通过pre指向前一个结点,通过next指向后一个节点。最终实现双向链表。
- LinkedList的元素的添加和删除,不是通过数组完成的,相对来说效率较高。
- 模拟一个简单的双向链表如下代码:
package com.study.srv.demo13;
/**
* @author Wen先森
* @version 1.0
* @date 2022/3/31 14:43
*/
public class Demo11 {
public static void main(String[] args) {
Node jack = new Node("jack");
Node tom = new Node("tom");
Node rose = new Node("rose");
jack.next=tom;
tom.next=rose;
rose.pre=tom;
tom.pre=jack;
Node first=jack;
Node end=rose;
while (true){
if (first==null){
break;
}
System.out.println(first);
first=first.next;
}
System.out.println("插入一个bob");
Node bob = new Node("bob");
tom.next=bob;
bob.next=rose;
rose.pre=bob;
bob.pre=tom;
while (true){
if (end==null){
break;
}
System.out.println(end);
end=end.pre;
}
}
}
class Node{
public String item;
public Node pre;
public Node next;
public Node(String item) {
this.item = item;
}
@Override
public String toString() {
return "Node{" +
"item='" + item + '\'' +
'}';
}
}
ArrayList和LinkedList比较
List实现类 | 底层结构 | 增删的效率 | 改查的效率 |
ArrayList | 可变数组 | 较低,数组扩容 | 较高 |
LinkedList | 双向链表 | 较高,通过链表追加 | 较低 |
如何选择ArrayList和LinkedList:
- 如果我们改查的操作多,选择ArrayList。
- 如果我们增删的操作多,选择LinkedList.
- 一般来说,在项目中,80%-90%都是查询,因此大部分选择ArrayList
- 在一个项目中,根据业务灵活选择,也只能这样,一个模块使用的是ArrayList,另一个模块是LinkedList,也就是根据业务来进行选择。
Set接口
- Set接口也是Collection接口的子接口。
- Set集合类的元素是无序的(添加和取出的顺序不一致),没有索引。但是LinkedHashSet可以保证元素添加的顺序,TreeSet保证元素自然的顺序。
- 不允许重复元素,所以最多只包含一个null。
- Set接口所有已知实现类:AbstractSet, ConcurrentSkipListSet, CopyOnWriteArraySet, EnumSet, HashSet, JobStateReasons, LinkedHashSet, TreeSet
- Set接口常用实现类:HashSet、TreeSet。
- Set接口常用方法
跟List接口一样,Set接口也是Collection接口的子接口,因此常用方法和Collection接口一样。
- Set接口的遍历方式
- 可以使用迭代器
- 增强for循环
- 不能使用索引的方式来获取。
HashSet实现类
-
HashSet实现了Set接口。
-
HashSet实际上是HashMap。
public HashSet() { map = new HashMap<>(); }
-
HashSet可以存放null值,但是只能存在一个
-
HashSet不保证元素是有序的,取决于Hash后,再确定索引的结果(即,不保证存放元素的顺序和取出顺序一致)
-
不能有重复数据/对象。
HashSet底层是HashMap,HashMap底层是(数组+链表+红黑树)。
HashSet添加元素底层实现:
- HashSet底层是HashMap
- 添加一个元素时,先得到hash值,hash值会转换为索引值。
- 找到储存数据表table,看这个索引位置是否已经有存放的元素。
- 如果没有,直接加入。
- 如果有就用eqlus比较,如果相同就放弃添加,如果不同添加到最后。
- 在Java8中,如果一条链表的元素个数到达TREEIFY_THRESHOLD(默认是8),并且table的大小>=MIN_TREEIFY_CAPACITY(64)就会进行树化(红黑树)。
HashSet扩容和转成红黑树机制:
- HashSet底层是HashMap,第一次添加时,table数组扩容到16,临界值(threshold)是16*加载因子(loadFactor)是0.75=12.
- 如果table数组使用到了临界值12,就会扩容到16*2=32,新的临界值就是32*0.75=24,以此类推。
- 在Java8中,如果一条链表的元素个数到达TREEIFY_THRESHOLD(默认是8),并且table的大小>=MIN_TREEIFY_CAPACITY(64)就会进行树化(红黑树)。
LinkedHashSet实现类
- LinkedHashSet是HashSet的子类,也是Set接口的实现类。
- LinkedHashSet底层是一个LinkedHashMap,底层维护了一个数组+双向链表
- LinkedHashSet根据元素的hashCode值来决定元素的存储位置,同时使用链表维护元素的次序,这使得元素看起来是以插入顺序保存的。
- LinkedHashSet不允许添加重复元素。
Set set=new LinkedHashSet(); set.add(new String("A")); set.add("Q"); set.add("Q"); set.add(new Customer("西",101)); set.add(1); set.add("H");
说明:
- 在LinkedHashSet 中维护了哟个hash表和双向链表(LinkedHashSet 中有head和tail)
- 每一个节点有before和after属性,这样可以形成双向链表。
- 在添加一个元素时,先求hash值,再求索引,确定该元素在table中的位置,然后将添加的元素加入到双向链表中(如果已经存在,不添加【原则上跟HashSet一样】)。
- tail.next=newElement;
- newElement.pre=tail;
- tail=newElement;
- 这样的话,我们遍历LinkedHashSet也能确保插入顺序和遍历顺序是一致的。
TreeSet实现类
- TreeSet添加数据时,只能添加一种数据类型的数据。
- 使用treeSet集合存储自定义对象,无参构造器方法是通过自然排序对元素进行排序的。
- 自然排序就是让元素所属的类实现Comparable接口,重写compareTo()方法。
- Integer能排序(有默认排序),String能排序(有默认排序),自定义的类存储的时候出现异常(因为没有默认排序) 。
- TreeSet是依靠Treemap实现的,TreeSet是一个有序不可重复集合。TreeSet中的元素将按照升序排列,缺省是按照自然排序进行排列,意味着TreeSet中的元素要实现Comparable接口,或者自定义的比较器。
package com.study.srv.demo14;
import java.util.TreeSet;
/**
* @author Wen先森
* @version 1.0
* @date 2022/4/8 10:05
*/
public class Demo07 {
public static void main(String[] args) {
TreeSet treeSet=new TreeSet();
treeSet.add(1);
// treeSet.add("2");//treeSet只能添加一种类型的数据
treeSet.add(2);
treeSet.add(4);
treeSet.add(3);
treeSet.add(0);
System.out.println(treeSet);
TreeSet set=new TreeSet();
set.add("Alen");
set.add("Ben");
set.add("Jack");
set.add("Hlean");
System.out.println(set);
TreeSet set1=new TreeSet();
set1.add(new Son("jack",22));
set1.add(new Son("Mark",19));
set1.add(new Son("Alen",26));
set1.add(new Son("Cily",20));
System.out.println(set1);
}
}
class Son {
private String name;
private int age;
public Son(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Son{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
解决方案:
- Son类实现Comparable接口
- 重写 compareTo(Object o)方法
class Son implements Comparable{
private String name;
private int age;
public Son(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Son{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
public int compareTo(Object o) {
Son son=(Son)o;
int num=this.age>son.age ?1:(this.age==son.age ? 0:-1);//升序
//int num=this.age<son.age ?1:(this.age==son.age ? 0:-1); //降序
if (num==0){
num=this.name.compareTo(son.name); //当age一样时,根据name比较排序
}
return num;
}
}
也可以使用TreeSet的另一个构造器,即传入一个比较器(用匿名内部类)
package com.study.srv.demo14;
import java.util.Comparator;
import java.util.TreeSet;
/**
* @author Wen先森
* @version 1.0
* @date 2022/4/8 10:50
*/
public class Demo08 {
public static void main(String[] args) {
TreeSet set=new TreeSet(new Comparator() {
@Override
public int compare(Object o1, Object o2) {
Son1 son1=(Son1) o1;
Son1 son2=(Son1) o2;
int result =son1.getAge()>son2.getAge()?1 :(son1.getAge()==son2.getAge()?0:-1);
if (result==0){
result=son1.getName().compareTo(son2.getName());
}
return result;
}
});
set.add(new Son1("jack",22));
set.add(new Son1("Mark",19));
set.add(new Son1("Alen",26));
set.add(new Son1("Cily",22));
System.out.println(set);
}
}
class Son1 {
private String name;
private int age;
public Son1(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Son{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
HashSet和TreeSet的去重机制
- HashSet的去重机制:hashCode()+equals(),底层先通过存入对象,进行运算得到一个flash值,通过hash值得到对应的索引,如果发现table索引所在的位置,没有数据,就直接存放如果有数据,就进行equals比较[遍历比较],如果比较后,不相同,就加入,否则就不加入。
- TreeSet的去重机制:如果你传入了一个Comparator 匿名对象,就使用实现的compare去重,如果方法返回0.就认为是相同的元素/数据,就不添加,如果你没有传入一个 Comparator 匿名对象,则以你添加的对象实现的 Compareable 接口的compareTo去重.
Map接口
Map接口特点
JDK8的Map接口特点:
- Map与Collection并列存在。用于保存具有映射关系的数据:Key-Value(双列元素)。
- Map中的key和value可以是任何引用类型的数据,会封装到HashMao$Node对象中。
- Map中的key不允许重复,原因和HashSet一样。当有相同的key时,就等价于替换。
- Map中的value可以重复。
- Map中的key可以为null,value也可以为null,但要注意key中的null只能有一个,values中的null可以有多个。
- 常用String类作为Map的key,但并不代表只能用String类型的,只要是Object子类就可以用。
- key和value之间存在单向一对一关系,即通过指定的key总能找到对应的value。
- Map存放数据的key-value示意图,一对k-v是放在Node中的,因为Node实现了Entry接口,有些书也说一对k-v就是一个Entry。
containsKey:查找键是否存在。
keySet:获取所有的键。
entrySet:获取所有关系k-v。
values:获取所有值。
Map接口常用方法
方法名 | 方法说明 |
put() | 添加元素 |
remove() | 根据键删除映射关系 |
get() | 根据键获取值 |
size() | 获取元素个数 |
isEmpty | 判断个数是否为0 |
clear() | 清除 |
containsKey | 查找键是否存在 |
Map map=new HashMap();
//put添加
map.put("张翠山","殷素素");
map.put("张无忌","赵敏");
map.put("郭靖","黄蓉");
map.put("杨过","小龙女");
map.put("张无忌","周芷若");
System.out.println(map);
//remove 根据键删除映射关系
map.remove("张无忌");
System.out.println(map);
//get 根据键获取值
map.get("杨过");
System.out.println(map);
//size 获取元素个数
System.out.println(map.size());
//isEmpty 判断元素个数是否为0
System.out.println(map.isEmpty());
//clear 清除
map.clear();
System.out.println(map);
//containsKey 查找键是否存在
System.out.println(map.containsKey("杨过"));
Map接口遍历方式
根据keySet获取所有键遍历:
//根据keySet获取所有键根据键遍历
Set keySet = map.keySet();
//强循环
System.out.println("keySet强循环:");
for (Object key :keySet) {
System.out.println(key+"="+map.get(key));
}
//迭代器
System.out.println("keySet迭代器");
Iterator iterator=keySet.iterator();
while (iterator.hasNext()){
Object key=iterator.next();
System.out.println(key+"="+map.get(key));
}
根据value遍历:
//找到所有的value,根据value遍历
Collection values=map.values();
//增强for循环
System.out.println("Values强循环:");
for (Object value:values) {
System.out.println(value);
}
//迭代器
System.out.println("Values迭代器");
Iterator iterator1=values.iterator();
while (iterator1.hasNext()) {
Object next = iterator1.next();
System.out.println(next);
}
根据entrySet所有关系k-v遍历:
//可以获取所有的键映射,之后遍历
Set entrySet=map.entrySet();
//增强for循环
System.out.println("entrySet强循环:");
for (Object entry:entrySet) {
Map.Entry m=(Map.Entry)entry;
System.out.println(m.getKey()+"="+m.getValue());
}
//迭代器
System.out.println("entrySet迭代器");
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实现类
- Map接口的常用实现类:HashMap、HashTable和Properties。
- HashMap是Map接口使用频率最高的实现类。
- HashMap是以key-value对的方式来存储数据。
- key不能重复,但是value值可以重复,允许使用null键和null值。
- 如果添加相同的key,则会覆盖原来的key-val,等同于修改(不同的是,key不会替换,会替换val)。
- 与HashSet一样,不保证映射顺序,因为底层都是以hash表方式存储的。
- HashMap没有实现同步(方法没有互斥操作),因此线程不安全。
HashMap扩容机制
-
HashMap底层维护了Node类型的数组table,默认为null。
-
当创建对象时 ,将加载因子(loadfactor)初始化为0.75。
-
当添加key-val时,通过key的hash值得到在table数组中的索引。然后判断该索引是否存在元素,如果没有元素直接添加。如果该处索引有元素,继续判断该元素的key是否和准备加入的key相等,如果相等,直接替换val;如果不相等需要判断是树结构还是链表结构,做出相应处理。如果容量不够,则需要扩容。
-
第一次添加,需要扩容table容量为16,临界值(threshold)为12.
-
以后再次扩容,则需要扩容table容量为原来的两倍,临界值为原来的2被,即24,以此类推。
-
在java8中,如果一条链表的元素个数超过TREEIFY_THRESHOLD(默认是8),并且table的大小>=MIN_TREEIFY_CAPACITY(默认64),就会进行树化(红黑树)。
HashTable 实现类
- 存放的元素是键值对:即k-v。
- hashtable的键与值都不能为null。
- hashtable使用方法基本和hashmap一样。
- HashTable是线程安全的,HashMap是线程不安全的。
HashTable扩容机制
-
Hashtable的扩容机制通过rehash()来实现,数组长度初始为11个,如果Hashtable中元素的个数大于临界值时,会调用rehash()来实现扩容。临界值的大小等于Hashtable数组的大小与负载因子相乘,默认的负载因子大小为0.75。
-
Hashtable扩容的数组长度是旧数组长度乘以2加1 。
-
Hashtable采用头插法的方式迁移数组。
HashMap与HashTable对比
实现类 | 版本 | 线程安全(同步) | 效率 | 允许null键null值 |
HashMap | 1.2 | 不安全 | 高 | 可以 |
HashTable | 1.0 | 安全 | 较低 | 不可以 |
Properties实现类
- Properties类继承自HashTable类且实现了Map接口,也是使用一种键值对的形式来保存数据。
- 使用特点与HashTable类似。
- Properties还可以用于从XXX.properties文件中,加载数据到Properties类对象,并进行读取和修改。
- XXX.properties文件中通常用做配置文件。
TreeMap
-
TreeMap 存储key-val键值对,底层通过红黑树实现。
-
TreeMap与TreeSet一样,都有默认的自然排序。
-
与HashMap相比,TreeMap是一个能比较元素大小的Map集合,会对传入的key进行大小排序,其中可以使用元素的自然排序,也可以使用集合中自定义的比较器来进行定义。
-
不允许插入重复的key。
-
可以插入null键、null值。
-
可以对元素进行排序,但是它是无序集合(插入和遍历顺序不一样)。
总结——选择集合实现类
在开发中,选择什么集合实现类,主要取决于业务操作特点,然后根据集合实现特性进行选择。
- 先判断存储的类型(一组对象【单列】或一组键值对【双列】)。
- 一组对象【单列】:Collection接口
- 允许重复:list
- 增删多:LinkedList【底层维护了一个双向链表】。
- 改查多:ArrayList【底层维护了Object类型的可变数组】。
- 不允许重复:Set
- 无序:HashSet【底层是HashMap,维护了一个哈希表,数组+链表+红黑树】。
- 排序:TreeSet
- 插入顺序和取出顺序一致:LinkedHashSet【底层维护数组+双向链表】。
- 允许重复:list
- 一组键值对【双列】:Map
- 键无序:HashMap【底层维护时哈希表,数组+链表+红黑树】。
- 键排序:TreeMap
- 键插入顺序和取出顺序一致:LindedHashMap。
- 读取文件:Properties。
Collections工具类
- Collections是一个操作List、Set和Map等集合的工具类。
- Collections中提供了一系列的静态方法对集合元素进行排序、修改和删除操作。
排序操作 | 方法说明 |
reverse(List) | 翻转List集合中元素的顺序 |
shuffle(List) | 对List集合元素进行随机排序 |
sort(List) | 根据元素的自然顺序对指定的List集合元素按升序排列 |
sort(List,Comparator) | 根据指定的Comparator产生的顺序对List集合进行排序 |
swap(List,int,int) | 将指定List集合中的i处元素和j处元素进行交换 |
查找、替换操作 | 方法说明 |
Object max(Collection); | 根据元素的自然排序,返回给定集合中的最大值。 |
Object max(Collection,Comparator); | 根据指定的Comparator产生的顺序,返回给定集合中的最大值。 |
Object min(Collection); | 根据元素的自然排序,返回给定集合中的最小值。 |
Object min(Collection,Comparator); | 根据指定的Comparator产生的顺序,返回给定集合中的最小值。 |
int frequency(Collection,Object); | 返回指定集合中指定元素的出现次数 |
void copy(List dest,List src); | 将src中的内容复制到dest中 |
boolean replaceAll(List list,Object oldVal,Object newVal); | 使用新值替换List对象的所有值 |
特别注意的是:void copy(List dest,List src);中dest的集合大小一定不能小于src集合的大小否则报IndexOutOfBoundsException异常。