概念
集合是用于存储对象的工具类容器,实现了常用的数据结构,提供了一系列公开的方法用于删除、修改、查找和遍历数据,降低了日常开发成本。
三种集合
Set
set集合中元素是无序、不可重复的
List
list集合中元素是从前到后遍历的
List分为ArrayList和LinkedList
Map
map集合采用键值对存储,一个键对应一个值
基本操作
public class Demo {
public static void main(String[] args) {
List<Integer> list=new ArrayList<>();//定义ArrayList集合
//add增
list.add(1);
list.add(2);
list.add(3);
//remove删
list.remove(2);
//set改
list.set(0,3);
//get取
list.get(0);
}
}
三种遍历方式
public class A {
public static void main(String[] args) {
//定义ArrayList集合
List<Integer> list=new ArrayList<>(1000);//开辟空间为1000,类型为Integer
//进行add操作 添加元素
list.add(1);
list.add(2);
list.add(3);
for(int i=0;i<list.size();i++){//循环遍历集合的值
System.out.println(list.get(i));//依次遍历输出元素的值
}
Iterator <Integer>it=list.iterator();//初始化迭代器
while (it.hasNext()){//判断下一元素值是否存在
Integer i=it.next();//遍历下一元素的值
System.out.println(i);//打印当前元素的值
}
for(int i:list){//利用foreach循环遍历元素的值
System.out.println(i);
}
//定义ArrayList集合并赋初值
List<Integer> list2= Arrays.asList(1,2,3,4,5);
//已经定义初值的集合不能对元素的个数进行修改
// list2.add(6);
// list2.remove(2);
// list2.clear();
list2.set(1,30);//set操作是修改元素的值,并没有改动元素个数
}
}
可变形参
public class Demo {
private int age;
private String name;
public void method1(int age1,String name1){
age=age1;
name=name1;
}
public void method2(int age1,int age2,String name1){
age1=age2;
age=age1;
name=name1;
}
public void method3(String name,int...age){
}
}
可变形参可以帮助解决重载问题,避免同一类型参数重复调用造成冗余
fail-safe
fail-fast
fail-fast存在多线程环境下,当线程1对集合进行遍历时,线程2在期间进行修改,造成线程1读取的结果发生变化
public class B {
public static void main(String[] args) {
List list=new ArrayList();//定义集合
list.add(1);
list.add(2);
list.add(3);
List list1=list.subList(0,2);
list.remove(0);
list.add(6);
// 输出子集合会报错,输出原集合不会报错
// System.out.println(list1);
}
}
上述代码中,两个list同时对其进行读写操作,控制台会报异常,启动sail-safe机制保证读写一致。
addAll可以看做是add操作的加强版,可以一次性将多个值一次存储到集合,避免了每次add都会进行的开堆操作。
public class C {
public static void main(String[] args) {
List<String> list=new ArrayList<>();//定义list集合
Collection c=new ArrayList();//定义集合
//向集合添加元素
c.add("A");
c.add("B");
c.add("C");
list.addAll(c);//addAll一次加入元素
Iterator<String> it=list.iterator();//定义迭代器
while (it.hasNext()){//判断是否还有下个元素
System.out.println(it.next());//打印下个元素
}
}
}
CopyOnWriteArrayList是基于ArrayList在多线程处理中出现异常而出现的。
CopyOnWriteArrayList在内部会进行加锁操作,在多线程时会复制一个新的集合,在原来的集合中进行读操作,而在复制集合中进行修改操作,修改操作完毕后将原集合指针指向复制集合,原集合做删除操作。
public class C1 {
public static void main(String[] args) {
List<String> list=new ArrayList<>();//定义list集合
//向集合依次添加元素
list.add("A");
list.add("B");
list.add("C");
List<String> list2=new CopyOnWriteArrayList<>();//定义COW集合
list.add("D");
Iterator<String> it=list.iterator();//定义迭代器
while (it.hasNext()){//判断是否由下一个元素
System.out.println(it.next());//进行打印
}
}
}
CopyOnWriteArrayList的内存占用很大,所以不适合进行多次修改操作
原来集合内存占用:100M
复制集合占用:100M
插入大小占用:10M
总共:100+100+10=210M
LinkedList是双向链表,LinkedList插入和删除快,但查找慢。
public class D {
public static void main(String[] args) {
Queue<String> q=new LinkedList<>();//定义Linked集合
//依次输入元素的值
q.offer("北京");
q.offer("上海");
q.offer("广州");
String str="";//定义字符串
while ((str=q.poll())!=null){//判断是否为空
System.out.println(str);//打印元素的值
}
}
}
ArrayList与LinkedList比较
ArrayList底层采用顺序表进行存储,所以查找容易,增删改操作效率较低
LinkedList底层采用双向链表进行存储,所以增删改操作容易,但查找不如ArrayList
HashMap在JDK8.0前是是顺序表+链表
JDK8.0后是顺序表+链表+红黑树
slot哈希槽:位置标识,类似指针对应数组的下标
table顺序表:是一个数组,做bucket的表头
bucket哈希桶:用链表存储对应的元素的值
public class E {
public static void main(String[] args) {
Map<String,Object> map=new HashMap(100);//定义HashMap
map.put("北京",100);
map.put("上海",200);
map.put("天津",300);
map.put(null,null);
List list=new ArrayList();//定义list集合
list.add("张");
list.add("王");
list.add("李");
Iterator it=map.keySet().iterator();//定义迭代器
//输出键
while (it.hasNext()){
System.out.println(it.next());
}
System.out.println();
Iterator it2=map.values().iterator();//定义迭代器
//输出值
while (it2.hasNext()){
System.out.println(it2.next());
}
}
}
HashMap存在的问题
1.数据丢失
两个线程同时进行修改操作时,后一个操作会覆盖前一个,造成对象丢失
2.已遍历区间新增元素会丢失
使用迁移方法时,新增的元素会落在已遍历过的哈希槽上,在遍历完成后,table数组引用指向了newTable,这时新增元素会被当做垃圾回收
3.新表会被覆盖
4.迁移丢失
ConcurrentHashMap采用volatile关键字而不使用加锁方法,牺牲部分效率,但性能较好
ConcurrentHashMap利用锁分段技术加强锁的数量,使争夺同一把锁的线程数目得到控制。
使用锁分段技术,在面对大量数据的时候,想要处理部分数据不需要对整体加锁(这样频繁操作会大大降低效率),而是采用锁分段技术对每段数据进行加锁,这样只需要对该部分进行解锁,而不改变其他数据段,从而提高效率
但在面对size(判断大小)containsValue(查找值)等操作需要遍历整体才能得出结果时,还是需要对整体进行加锁
ConurrentHashMap由Segment和HashEntry组成。
Segment是一种可重入锁
HashEntry用来存储键值对数据
Segment结构和HashMap类似,一个Segment包含一个HashEntry数组
Set中不允许出现重复元素
常用HashSet、TreeSet、LinkedSet等