List
元素有序,且可重复
遍历方法:1-for循环通过下标遍历 2-foreach遍历 3-使用迭代器遍历 4-使用lambda遍历
扩容:初始容量10,负载因子0.5,扩容增量0.5被 新容量=旧容量+旧容量*0.5或者新容量=旧容量*1.5
实现
ArrayList
1 简单数据结构,超出容量会自动扩容。所以在我们看来ArrayList的空间是无线大的,动态数据
2 内部实现是基于基础的对象数组
3 随机访问块
4 不适合随机的增加或者删除(因为ArrayList是一个对象数据,数据一旦要修改整个数据位置都会改动)
5 线程不安全(因为整个方法没有锁)
List<Student> ls=new ArrayList<Student>();
/**
* student是我的一个实体类
*/
List<Student> ls=new ArrayList<>();
// 输出方式1 使用遍历输出
for (Student su : ls) {
System.out.println(su);
}
// 输出方式2 使用for循环输出
for (int i = 0; i < ls.size(); i++) {
System.out.println(ls.get(i));
}
//输出方式3 使用迭代器输出
Iterator<Student> lt= ls.iterator();
//判断是否有下一位
if(lt.hasNext()) {
//得到它的下一位
lt.next();
System.out.println(lt);
}
//输出方式4 使用lambda表达式
ls.forEach(l->System.out.println(l));
//BeFore是在每个测试方法之前执行
@Before
public void stati() {
ls.add(new Student("11",1));
ls.add(new Student("22",2));
ls.add(new Student("33",3));
ls.add(new Student("44",4));
ls.add(new Student("55",5));
ls.add(new Student("66",6));
}
//test方法代表的是你在运行这个方法时,就只能运行你所选中的方法。其他的test方法是不能运行的,这样比较好测试
@org.junit.Test
public void text01() {
//遍历 删除
//当使用遍历删除集合时,要用break来终止循环,否则报错
//因为你遍历的是原来的数组,你进行删除后。list这个集合里面的数据已经发生改变了,必须使用break来终止循环
for (Student s : ls) {
if(s.getSid()==1) {
ls.remove(s);
break;
}
}
//输出
ls.forEach(t->System.out.println(t));
}
@org.junit.Test
public void text02() {
//使用迭代器进行删除
Integer a[]= {1,3,65,3,23,231,5,4,9};//定义一个数组
List<Integer> ls=Arrays.asList(a);//把数组放到一个集合里面
Iterator<Integer> it= ls.iterator();//使用 迭代器
while(it.hasNext()) {//判断是否有下一个
Integer s= it.next();//如果有下一个值
if(s==1) {//判断值是否为1
it.remove();//如果等于就删除
//执行完毕后会报错,因为Arrays里面没有remove这个方法 可以看看源代码。但是我们为什么可是使用remove方法代码而不报错,因为Arrays里面继承了Arraylist接口。
//所以我们必须使用下一种方法
}
}
}
@org.junit.Test
public void text03() {
//使用迭代器删除
Integer a[]= {1,3,4,2,5,4};
List<Integer> ls=new ArrayList<>(Arrays.asList(a));//这里我使用的是Arraylist,在ArrayList里面放了一个Arrays集合。这样我们就不会报错了
Iterator<Integer> it= ls.iterator();
while(it.hasNext()) {
Integer s= it.next();
if(s==1) {
it.remove();
break;
}
}
ls.forEach(e->System.out.println(e));
}
@org.junit.Test
public void text04() {
//使用流的方式删除
//从第一个开始,如果id不为1,则就保存到新的list集合中 filter过滤条件
List<Student> a= ls.stream().filter(t-> t.getSid()!=1).collect(Collectors.toList());
//输出
a.forEach(e->System.out.println(e));
}
//java的引用传递
@org.junit.Test
public void text05() {
//当我们需要对一个集合进行修改时,要保证之前的集合不会被修改就需要使用深度拷贝
List<Student> lss=updata02(ls);
lss.forEach(s->System.out.println(s));
System.out.println("==========");
ls.forEach(a->System.out.println(a));
}
//深度拷贝(克隆)
//1使用json传递
private List<Student> updata02(List<Student> o ){
String json= JSON.toJSONString(o);//这里需要一个json工具包,我这里使用的是阿里的json工具包fastjson
//复制一个新的集合
List<Student> ls= JSON.parseArray(json,Student.class);
//开始遍历
for (Student s : ls) {
s.setName("1111");
}
return ls;
}
LinkedList
1 LinkedList额外提供了一些方法。比如addFirst 在集合最前面增加一组数据,addLast 在集合最后面增加一组数据,以及remove和insert方法等等
2 LinkedList可用作堆栈(stack)包括了push和pop方法 队列queue 或双向队列deque
3 以双向对象实现,所以能够很好的实现增加或者删除。链表无容量限制,允许元素为空
4 适合最随机的增加或者删除
5 线程不安全(方法里面没有锁)
java.util.LinkedList<Student> l=new java.util.LinkedList<>();
@Test
public void text01() {
//Linkedlist的方法基本与ArrayList方法一致
l.addFirst(new Student("1", 1));//在集合的最前面增加一组数据
l.addLast(new Student("2", 2));//在集合的最后面增加一组数据
}
Vector
线程安全(说白了在每个方法里面都加了锁)并行效率慢,不建议使用。方法与ArrayList差不多
CopyOnwiterArrayList
1 写时复制
2 线程安全
3 适合读多写少的场景(我查阅了很多数据,在数据很多的适合还是不适合写)
4 写时复制一个新的数据,在这个新的数组里面完成增加或者修改删除,后将新数组赋值给旧数组。最终完成数据一致性
5 比Vector性能高(虽然copyonwiterarraylist里面的方法也有所,但是它是写时赋值一个新的数组。在用户未完成新数组修改的方法后,其他用户还是访问的是旧数组。两边都不耽误)
6 最终一致性
7 继承了List接口,使用方法与ArrayList基本一致(你只要会用ArrayList就会copyonwiterarraylist)
Set
特点:无序,不重复
遍历:foreach,迭代器,下标,lambda表达式。方法基本与list一致
扩容:初始容量为16,负载因子0.75。扩容增加一倍
实现
HashSet
1 它存储唯一元素并允许空值,依据对象的hashcode来判断该元素是否存在
2 由HashMap支持
3 不保存插入顺序
4 非线程安全(方法也没有锁)
private Set<Student> s=new HashSet<>();
@Before
public void set() {
//增加
s.add(new Student("1", 1));
s.add(new Student("2", 2));
s.add(new Student("3", 3));
s.add(new Student("4", 4));
s.add(new Student("5", 5));
}
@Test
public void text01() {
//注意,要在对象里面实现equals和hashCode方法。要不然还是会能增加重复的数据
s.add(new Student("1", 1));//我这里已经实现了这两个方法,所以数据不会重复增加
s.forEach(t->System.out.println(t));
}
//Set深度拷贝
@Test
public void text05() {
String json= JSON.toJSONString(s);
Set<Student> p= JSON.parseObject(json,new TypeReference<Set<Student>>() {});
for (Student s : p) {
if(s.getSid()==1) {
p.remove(s);
break;
}
}
System.out.println("之前的数据");
s.forEach(z->System.out.println(z));
System.out.println("现在的数据");
p.forEach(c->System.out.println(c));
}
TreeSet
1 是一个有序的,且不包含重复的额元素的集合
2 作用是根据根据有序的Set集合,自然排序或者根据提供的Comparator进行排序
3 TreeSet是基于TreeMap实现的
Map
特点:无序,键值对。键不可以重复,值可以重复。键如果重复则覆盖,没有继承Collection接口
扩容:初始容量16,负载因子0.75,扩容增量1倍
遍历:1-先获取所有键的Set集合,再遍历(通过键来获取值)2-取出保存好的Entry的Set,再遍历Set即可
实现
HashMap
线程不安全,但是速度最快,最常用
内部采用对象数组存放数据
流程图中绿色标出的部分为JDK8新增的处理逻辑,目的是在Table[i]中的Node节点数量大于8时,通过红黑树提升查找速度。
put执行过程
在使用put的方法时,Map中的table数组会使用hashcode方法来计算写入的Key值到底该数组存储table中什么位置。前提:在实体类中写入hashcode与equals方法
在我们使用get方法的时候也是类似,首先会通过hashcode计算我们需要得到的数据会保存在哪里。如果table[i]的存储的数组只有一个,直接会把该数据得到。如果里面的数据不止一只,那它就会通过遍历的方式,一个一个来遍历,最终得到想要的结果。因为table[i]中存储的是对象数组是链表结构。在jdk8之后就开始不同了,如果你的table[i]中的数组超过了8个,那它就会使用红黑树的方式来存储数据。这样的存储方式会让数据能够更好的遍历,因为不需要一个一个找了,所以速度会加快。前提:要在jdk8之后的版本才有这个功能,在jdk8版本之前都是数组的形式来存储数据的
Table数组中的的Node
链表结构示意图
红黑树示意图
java.util.Map<String, Object> p=new HashMap<>();
@Before
public void befor() {
//增加
p.put("1", 1);
p.put("2", 2);
p.put("3", 3);
p.put("4", 4);
p.put("5", 5);
}
@Test
public void text01() {
//map键不能重复,值可以
p.put("1", 6);
//如果键重复则会覆盖
System.out.println(p);
}
//使用迭代器遍历
@Test
public void text02() {
//得到键
Iterator<String> i= p.keySet().iterator();
while(i.hasNext()) {
String s= i.next();
//通过键输出对应的值
System.out.println(p.get(s));
}
}
//使用EntrySet遍历
@Test
public void text03() {
Iterator<Entry<String, Object>> i= p.entrySet().iterator();
while(i.hasNext()) {
Entry<String, Object> e= i.next();
System.out.println(e.getKey()+e.getValue());
}
}
//lambda表达式遍历
@Test
public void text04() {
p.forEach((k,v)->System.out.println(k+v));
}
//增加不存在的数据
@Test
public void text05() {
//如果Map集合里面没有5这个键,则增加
if(!p.containsKey("5")) {
p.put("5", 5);
}
//第二种方法
//缺席,如果4这个键位有则不增加。如果没有则增加这个键位
p.putIfAbsent("4", 4);
}
HashTable
线程安全,不太常用(用一把锁锁住所有,性能慢)方法与HashMap差不多
ConcurrentHashMap
线程安全,性能比HashTable高(因为是分段锁,而且还实现了cis接口)方法与HashMap差不多
TreeMap
1 Key值按一定的顺序排序
2 添加或者获取元素比ConcurrentHashMap慢。因为需要维护内部的红黑树,用于保证Key值的顺序
java.util.TreeMap<String, Object> t;
//从小到大排序
@Test
public void text02() {
//自己本身自带的排序
t=new java.util.TreeMap<>();
t.put("5", 5);
t.put("4", 4);
t.put("3", 3);
t.put("2", 2);
t.put("1", 1);
t.forEach((k,v)->System.out.println(k+":"+v));
}
//按key值进行排序
@Test
public void text01() {
t=new java.util.TreeMap<>(new Comparator<String>() {//Comparator比较器
//当我们在方法时使用了匿名函数,同时在实体类是实现了Comparable。匿名函数的优先级别会更高
@Override
public int compare(String a, String b) {
//比较 a>b返回正数 a=b 返回0 a<b返回负数
return b.compareTo(a);//如果是a.compareTo(b)则是从小到大排序,如果是b.compareTo(a)则是从大到小排序
}
});
t.put("5", 5);
t.put("4", 4);
t.put("3", 3);
t.put("2", 2);
t.put("1", 1);
//输出
t.forEach((k,v)->System.out.println(k+v));
}
//排序
@Test
public void text04() {
TreeSet<Student> t=new TreeSet<>(new Comparator<Student>() {
@Override
public int compare(Student a, Student b) {
// TODO Auto-generated method stub
return b.getSid()-a.getSid();//倒序
}
});
t.add(new Student("1", 1));
t.add(new Student("2", 2));
t.add(new Student("3", 3));
t.add(new Student("4", 4));
t.add(new Student("5", 5));
t.forEach(y->System.out.println(y));
}
//最后一种方法可以通过在实体类中写入Comparator
@Test
public void text05() {
TreeSet<Student> s=new TreeSet<>();
s.add(new Student("1", 1));
s.add(new Student("2", 2));
s.add(new Student("3", 3));
s.add(new Student("4", 4));
s.add(new Student("5", 5));
s.forEach(i->System.out.println(i));
}
LinkedHashMap
继承HashMap
LinkedHashMap是有序的,而且默认为插入顺序(与你怎么写就怎么输出)但我们需要有序的存储Key或者value是就使用LinkedHashMap