定义
用于存储、操作任意类型对象
的容器。
集合的底层是:数组
数组的弊端
- 声明数组后,长度固定不可变
- 没有提供查看
有效元素个数
的方法,容易导致空指针
集合的特点
- 集合的长度是可变的
底层是数组,初始长度为10
当存满时,java自动扩容50%,并以新容量创建新数组—>封装成集合
- 集合可以存储 任意类型的对象
如123 底层自动装箱。其实有了包装类。大多基本类型数据都可以存储了。
- 集合只能存储对象
Collection框架
java.util.Collection : 集合层级的根接口
1. |--java.util.List : 有序的,并且允许重复
List 体系集合都有索引值 元素按索引值有序存储,当然也就可以重复

- |----ArrayList : 采用数组结构存储元素。
数组结构:
有索引
根据索引值对元素操作
因此:
查询操作多时,选择ArrayList存储数据,
因为可以根据索引值直接查询到所需的元素
若是增删则耗性能,在操作的索引位置开始,改变后面的元素索引
- LinkedList :采用双向链表结构存储元素。
双向链表结构:
采用前向与后向索引维护
增删操作只需维护前后索引
因此:
增删操作多时,选择。添加或删除直接在两端操作即可。
- Vector :线程安全的,古老的
2. java.util.Set : 无序的,并且不允许重复的
Set集合是通过hashCode()把hash值转换为索引值从而进行存储对象 而转换的索引值是不确定的,所以是无序的,并且可能存在重复转换的索引值。 所以不能重复存储 那么问题就来了: 1)怎么判断元素是否重复? 采用哈希算法 2)相同索引值而不重复元素怎么处理? 这种情况称为碰撞,通过单向链表处理
- HashSet :是 Set 接口的典型实现类
采用数组(专业称为:Hash表)+单向链表结构存储元素
HashSet 判断元素是否重复的依据:采用哈希算法
- LinkedHashSet :
相较于 HashSet 改为用链表维护元素的顺序。增删效率低于 HashSet
遍历效率高于 HashSet ,因他每个元素以链表维护,遍历时后面元素直接取出,不需再判断有没有
- TreeSet :
采用二叉树结构
实现了Comparable接口,拥有排序
什么是哈希算法
当向Set中添加对象时,java会先调用此对象所在类的hashCode()方法将hashCode值转换为随机的索引值。
- 若此索引值位置没有对象存储,则此对象存储到此位置。
- 若此索引值位置已有对象存储,则通过equals()比较这两个对象内容是否相同
若相同,视为重复不可再添加
若不相同,则依靠单向链表存储。这种情况叫碰撞
TreeSet排序原理
- 其实现了Comparable接口,提供自然排序功能
ComparTo()两个对象比较,返回int 1 -1 0,1代表大于,-1代表小于,0代表相等
- 当 TreeSet中存储元素是String、Interge类的对象时,内部已重写了ComparTo()。TreeSet会自动给排序
- 当TreeSet中存储元素是我们写的类,比如Person,那么TreeSet就不知道怎么排了,我们可以做定制排序,两种方式:
①自然排序(Comparable接口):
1)把添加到 TreeSet 集合中对象的类实现 Comparable 接口
2)重写 compareTo(Object o1) 方法
②定制排序(Comparator接口):
1)声明一个类实现 Comparator 接口
2)重写 compare(Object o1, Object o2) 方法
3)将该实现类的实例作为参数传递给 TreeSet 的构造器
Collection常用方法
-
add(Object obj) : 添加元素到集合中
-
size() : 获取集合中有效元素的个数
-
clear() : 清空集合中所有元素
-
isEmpty() : 判断集合中是否为空
-
contains(Object obj)
判断依据:根据对象所在的类的equals()方法进行判断
注意:如存入集合中对象是自定义的,要求自定义类要重写equals() -
addAll(Collection c) : 将 c 集合中所有的元素添加到当前集合中
-
containsAll(Collection c) : 判断 c 集合中所有的元素是否包含在当前集合中
-
remove(Object o) : 删除集合中指定元素
-
removeAll(Collection c) : 删除 c 集合中所有的元素
-
retainAll(Collection c): 取交集
-
toArray() : 将集合转换为数组
12.hashCode()
13.iterator() 迭代器
List常用方法
除了继承Collection,自己的方法有
- void add(int index, Object ele)
- boolean addAll(int index, Collection eles)
- Object get(int index) : 获取指定索引位置的元素
- int indexOf(Object obj):返回给定对象在list中的索引值
- int lastIndexOf(Object obj)
- Object remove(int index) : 删除指定索引位置的元素
- Object set(int index, Object ele) : 将指定索引位置的元素,替换成 ele
- List subList(int fromIndex, int toIndex):截取元素,包头不包尾
LinkedList常用方法
除了继承Collection,自己的方法有
- addFirst();
- addLast();
- getFirst();
- getLast();
- removeFirst();
- removeLast();
set常用方法
与Collection接口相同
集合的遍历
- 增强 for 循环
for(被遍历集合中元素的数据类型 变量名 : 被遍历的集合){
}
for(Object obj : list){
System.out.println(obj);
}
- 使用 Iterator 迭代器
迭代器接口为每个集合都提供迭代器,使用时以实例调用,java返回属于当前集合类的迭代器
//①获取 当前 集合 的迭代器
Iterator it = list.iterator();
//②通过 hasNext() 与 next() 方法遍历集合元素
while(it.hasNext()){
Object obj = it.next();
System.out.println(obj);
}
注意1
通常一个 hasNext() 方法配合一个 next() 使用
因迭代器有标记指针,当it.hasNext()时,会判断下一个位置是否有元素
有则Object obj = it.next() 进行赋值
若两个next() 同时使用时,上一个指针位置赋值后未有操作,紧接着操作下一个
it.next(),即遍历漏了
注意2
hasNext():是否有更多元素
next():下一个元素
- 列表迭代器: ListIterator (是 List 集合特有的迭代器)
@Test
public void test2(){
List list = new ArrayList();
list.add("AA");
list.add("BB");
list.add("CC");
list.add("DD");
list.add("EE");
//需求:判断若集合中元素为 "BB" , 在该元素位置添加一个 "bbb"
ListIterator li = list.listIterator();
while(li.hasNext()){
Object obj = li.next();
if(obj.equals("BB")){
li.add("bbb");
}
}
for (Object o : list) {
System.out.println(o);
}
}
HashSet系列问题
-
hashCode值转换索引值出现相同的原因?
底层数组(哈希表)长度是固定的,hashCode值虽通过转换
但得到的结果一定是存在长度的索引值。不然放哪。
比如长度为5,那么转换的索引值必须是0,1,2,3,4 -
导致碰撞的发生?
hashCode转换索引值出现相同,而对象的内容不同 -
Set为什么可以发生碰撞?
底层结构:数组(哈希码表)+ 单向链表
当碰撞时,将依赖链表结构,把碰撞对象加在原对象之后 -
若HashSet底层数组长度存满后如何处理?
不会出现存储满,因java通过判断,当数组存储到加载因子0.75时即以原数组*2进行自动扩容。
扩容后,碰撞位置中的所有元素将重新转换。再分配存储!(jdk 1.7)
碰撞是不可避免的,我们应尽量避免碰撞
-
为什么hashCode() 与 equals()要一并重写?
若是无重写hashCode(),使用的还是Object类的hashCode(),得到不同的唯一hashCode值
即使我们创建的实例内容相同,hashCode值亦不相同。那么再转换为索引值时,产生的索引值也不同
那么,即使是重复的对象,也将直接存入。
当重写hashCode()后,内容相同的实例得到的值必须一样。再转换为索引值时,他们的索引值也一样。 -
hashCode() 与 equals()的重写?
快捷生成即可。然后根据自己的需求修改
removeAll的巧用记录
public static void main(String[] args) {
ArrayList<Integer> var1 = new ArrayList<>();
ArrayList<Integer> var2 = new ArrayList<>();
var1.add(1);
var1.add(3);
var1.add(4);
var2.add(2);
var1.add(2);
var1.removeAll(var2);
System.out.println(var1.toString());
var2.add(4);
var1.removeAll(var2);
//是否还 == 原memberList?是的
System.out.println(var1.toString());
}
循环内删除元素
java语言中,for循环有三种实现方法 :
1、for循环遍历list
2、增强for循环
3、iterator遍历
注意:只有第三种可以正确的在for循环中删除对应元素,否则会抛null指针
第一种方式,由于ArrayList底层使用数组方式实现,当删除其中某一元素时,其余数组下标会前移,导致继续进行删除时会略过下一元素,最终的结果也会异常
第二种方式,会抛出ConcurrentModificationException的错误,即并发修改异常,具体异常原因可参考《Java ConcurrentModificationException异常原因和解决方法》
使用迭代器(官方推荐)
Iterator<Integer> it = intList.iterator();
where(it.hasNext()){
if(it.next() == 13){ //it.next()方法即可返回当前元素
it.remove();
}
要注意的是,必须使用iterator的remove方法,如果用list的remove方法同样会报上面提到的ConcurrentModificationException错误。
网友评论