目录
前言
集合是什么?
- 把一堆数据集中合并在一起进行统一的操作。
框架是什么?
- 为了解决某个问题,而预先设计的一系列具有继承或实现关系的类与接口。
Java Collection Framework(集合框架)
- 解决集合操作问题而预先设计好的一系列类与接口。
整个集合框架分为两个部分:
1、装东西的容器 -- 集合类;
2、为容器提供算法的操作类 -- 工具类。
集合类
Collection接口 是整个集合框架的核心接口。
它衍生出集合框架的四大子接口(四种不同的容器)
1、List接口 (直接从Collection接口衍生)
2、Set接口 (同上)
3、Map接口 (间接从Collection接口衍生)
4、Queue接口(直接从Collection接口衍生)
一. List**
特点:
线性(有序)
通俗讲就是,放到List当中的元素是有顺序的,它最大的特点就是有下标。
常用的List接口主要是两个类:ArrayList 和 LinkedList。
所有的List实现类都是实现的同一个List接口,所以它们的常用方法都是被直接定义在接口当中的,具有一致性。
ArrayList
底层用的就是数组
(1)产生List集合对象
//父类(接口)的引用 指向 子类(实现类)的对象
List lst=new ArrayList();
(2) 放入数据
//放数据
lst.add("hello");
lst.add((new Date()));
lst.add(100); //放入的是 自动封箱封装的一个Integer对象
//按add的顺序进行存放 (线性特点)
(3)增加add()
lst.add(2,"haha");
lst.add(5,"haha");
注意:
1.增加的位置如果有元素,该元素会被后移一位
2.增加的位置 前面 有空位置 会报错
(4)获取get()
Object object=lst.get(1);//通过下标获取
if (object instanceof String str){
System.out.println(str);
}else {
System.out.println("类型不一致");
}
(5)修改get()
lst.set(0,"Java");//通过下标修改指定位置的元素
System.out.println(lst.get(0));
(6)删除remove()
lst.remove("Java");//根据对象进行删除
lst.remove(1);//根据下标进行删除
lst.remove(Integer.valueOf(100));
如果List中全是数字,怎么按元素删除?
给remove方法 传入该数字的包装类对象
因为List 的两个方法,一个按int接受 下标 ;一个按object接受 对象
删除 整数元素 需要清楚 List里面存放的不是基本数据类型 而是它的包装类对象
(7)获取元素的个数size()
System.out.println("List中有:"+lst.size()+"个");
(8)List的遍历
1.List有下标 所以可用 for循环遍历
for(int i=0;i< lst.size();i++){
System.out.println(lst.get(i));
}
2.集合框架特有的循环方式---- 迭代器 Iterator (过于复杂)
注意:
(1)迭代器只能从头到尾访问
(2)迭代过程中 不能改变集合容器当中的元素个数
//将集合中的所有元素 全部导入迭代器中
Iterator it=lst.iterator();//来自Collection接口 List/Set都有此方法
while (it.hasNext()){//获取当前迭代器中 有无元素 有返回true 没有返回false,结束循环
Object value=it.next();//从迭代器中 取出 当前元素
System.out.println(value);
}
3.for-each
注意:
(1)它是语法糖,编译之后,它还是会成为原始语法。
(2)集合框架类的for-each 底层:编译以后是迭代器的语法 循环过程中 也不能改变集合容 器当中的元素个数
for(Object value:lst){
System.out.println(value);
}
for ( 变量类型 变量名 : 数组名 ) {
需要执行的循环语句;
}
LinkedList
底层实现没有使用数组,而是使用的链表结构
链表结构 采用的散列空间进行存放;
数组:
元素1|元素2|元素3...
双向链表:
[null]元素1[引用2] [引用2]元素3[引用4][引用3]元素4[引用5]
[引用1]元素2[引用3]
每个元素的后面有一个存放引用的空间,该空间的引用指向下一个元素;最后一个元素指向null。
用这种方式来表示每个元素之间的顺序关系。
ArrayList 和 LinkedList的差异
查询元素时,ArrayList效果更高 (LinkedList要顺着链条逐个查找)
增加删除元素时,LinkedList效果更高 (直接打断链条就删了)
ArrayList 和 Vector
Vector虽然名字中没有List,但它也是List接口下的一个实现类,且它的底层也是用的数组的方式。
Vector是线程安全的;ArrayList是不安全的(性能更好)
API文档中描述的它是同步的;
同步是线程安全的实现手段之一,某个类的描述说它是同步的,那么就说明它是线程安全的。
使用泛型类/方法
在实际开发当中,我们发现,通常我们是不会把不同数据类型的元素 放到一个集合当中去的,就算要放也有一定的要求,比如:你不会把座椅板凳和学生对象放在一个大集合中,做统一的处理;通常都还是要进行归类。在这种情况下,操作装Object的集合就不是那么方便了,我们需要把取出来的元素进行判定,判定它到底是哪种类型,然后再进行类型转换,才能调用到这种类型的特有行为。非常不方便。因此,才有了泛型。
“< > ”限制集合类内部允许装什么类型的元素
再去操作数据时,就只能操作这个类型的元素
List<String>stringList=new ArrayList<String>();
stringList.add("江小白");
String str=stringList.get(0);
语法细节: 1.<>里 只能书写一个类型 因为先人只定义了一个E 2.<>里 不能写基本数据类型 因为在集合中所有类型都是对象 所以要写成对应的包装类类型 3. “钻石语法” 第二个<>里 可以不写类型 注意: Java中的泛型是“假泛型”,也就是说Java中的泛型检查只是由编译器去检查,到运行期就不会检查了 泛型是函数式编程的语法特点 它跟形参与实参不同(只知道数据类型,不知道数据值,需要外部者传入)【数据参数化】 泛型连要操作什么样的类型都不知道,需要外部的使用者把这个数据类型传递进来【类型参数化】
二. Set
特点:
元素不能重复;且不保证存放的顺序就是你放入的顺序。
HashSet
Set集合的常用类通常都是HashSet
1.增加
最大特点就是不能放置重复元素
HashSet是如何判断两个元素是否重复的?
(1)HashSet先调用元素对象的hashcode方法,看两个元素的哈希值是否相等,如果不等那肯定是不同对象。
(2)如果相等,再根据两个元素对象的equals方法判断是否返回true,为ture则被认为是相同对象,不能重复添加,为false则可以添加。
hashcode 和 equals的关系?
hashcode是JVM用来判断两个对象在 物理位置上是否相等;
equals是用来判断两个对象在业务上是否相等;
常用类不用考虑重写,先人已经写好了
自定义类(由于先人不知道这些类的比较规则,所以需要我们去重写)
(1)通常在重写的时候,要求重写equals,也应该重写hashcode方法;
(2)在规范上,JVM要求equals方法返回true的两个对象,hashcode也应该是同一个值;
(3)而equals方法返回false的两个对象,hashcode也应该是不同的值.;
2、获取元素 -- Set集合没有提供
3、修改元素 -- Set集合没有提供
4、删除元素 -- remove() 只有根据对象进行删除,没有根据下标进行删除的方法; 根据对象进行删除的remove方法不支持泛型。
5、获取元素个数 -- size()
6、遍历 Set集合没有下标的概念,所以不支持普通for循环,只支持迭代器或for-each的语法。
通常我们只有在强调不能存放同一元素的这个场景中才会使用它。
三. Map**
特点:
所有元素以 键值对 的方式存放;key不能重复 。
常用的实现类 HashMap,另外还有可能出现的是Hashtable、以及可操作性文件的Properties
HashMap
由于Map集合不是从Collection集合上直接继承而来的,所以它的方法自成一套:
(1)产生HashMap集合
Map<String,StudentBean> stuMap = new HashMap<>();
(2)增加put
stuMap.put("J192001",new StudentBean("zhang3",22));
stuMap.put("J192002",new StudentBean("zhang4",21));
stuMap.put("J192003",new StudentBean("zhang5",20));
stuMap.put("J192004",new StudentBean("zhang6",18));
(3)获取 get
StudentBean stu0 = stuMap.get("J192003");
System.out.println(stu0);
通过键获取值,如果键不存在是直接返回null
(4)修改put
stuMap.put("J192003",new StudentBean("zhang7",30));
StudentBean stu1 = stuMap.get("J192003");
System.out.println(stu1);
只需要保证传入的key是你需要修改的那个元素对应的key
(5)删除remove
stuMap.remove("J192003");
StudentBean stu2 = stuMap.get("J192003");
System.out.println(stu2);
根据键去删除键值对儿,如果被删除的键不存在是不会报异常的
(6)获取元素个数 size
System.out.println(stuMap.size());
(7)判断键或值是否存在 -- containsKey、containsValue
(8)遍历: 由于在Map当中每个元素是有key和value共同组成,所以通常要么遍历所有的key,要么遍历所有的value。
- 获取所有的键 -- keySet
得到一个Set集合,里面全部是这个Map中的键; 然后我们可以通过遍历这个Set从而得到所 有的键;
- 获取所有的值 -- values
得到一个Collection集合,里面全部是这个Map中的值。 然后我们可以通过遍历这个 Collection从而得到所有的值。
HashMap 和 Hashtable
- HashMap 是线程不安全的,可以用null做键或值
- Hashtable 是线程安全的,不允许用null做键或值
怎样遍历所有的键值对呢?
Map有一个方法entrySet(),该方法会 返回一个Set集合,集合里面每个元素又是一个Entry对象,Entry对象里面装了一个键和值
注意:
1、泛型的表达 Set<Map.Entry<String,UserBean>>
2、Entry是一个定义在Map内部的类型,这涉及到了内部类的语法。
四. Queue
特点:
Queue 队列
先进先出 (可以理解为排队)
注意:
栈才是先进后出 (可以理解为 子弹上膛)
add() --- 放入成功返回true,放入失败(比如队列已满)则抛出异常
offer() ---失败直接返回false
element ---返回第一个元素,如果没有就报异常
peek ---没有,返回null
remove ---返回首元素,并从队列中删除,没有首元素报异常
poll --- 没有,返null,不报异常
put 添加一个元素 如果队列满,则阻塞
take 移除并返回队列头部的元素 如果队列为空,则阻塞
工具类
在集合框架中除了能够装东西的集合容器类以外,还给我们提供了工具类专门用来操作这些集合。
它们都是提供了一堆的静态方法,然后对传入的集合容器中的元素完成某个动作。
Collections
Collections操作的是集合框架中的容器类;主要是Collections及其下面的List、Set
Arrays
Arrays 操作的是数组
比较器
自定义类要想使用Collections中的最大、最小以及sort排序等跟比较有关系的方法,那么要求自定义类必须实现比较器,然后在比较器中告知比较的规则。
比较器有两种:
1、Comparable接口 -- 内部比较器; 它是由参与比较的对象自身去实现的,相当于自带的;
2、Comparator接口 -- 外部比较器; 它是独立于比较对象之外,在参与比较操作的时候给出的外部规则。
注意:
1、如果在比较的时候,传入了外部比较器,那么内部比较器不起作用了;就算没有实现内部比较器,也可以按外部比较器来进行排序;
2、我们把最常用的比较规则写到内部比较器当中,把用得少的比较规则写入到外部比较器当中;
3、如果外部比较器只用一次,那么可以采用匿名内部类的方式实现。