Java集合框架
在Java集合框架中
- 程序员不需要关心数组到底应该申请多少个元素,框架会自动分配空间.
- 程序员可以通过一个键,快速得到这个键所对应的值,并且不需要顾虑插入和删除元素的问题.
- 程序员可以方便地对数据进行排序,遍历等.
- 程序员可以直接使用各种数据结构.
Java集合框架:Java集合框架提供了一套性能优良,使用方便的接口和类,它们位于java.util包中.
List类集合
- ArrayList实现了长度可变的数组,在内存分配连续的空间.遍历元素和速记访问原色的效率比较高.
- LinkedList采用链表存储方式(离散空间),插入,删除元素时效率比较高.
如何使用ArrayList
1.创建ArrayList对象:
List dogs = new ArrayList(); //List是接口,不能直接创建对象
2.向List中添加对象:
dogs.add(ouou);
dogs.add(1,feifei) //可以指定添加位置,指定的位置必须在范围内,比在尾部添加效率低
3.删除对象:
Dog aDog = dogs.remove(1); //可能抛出IndexOutOFBoundsException
boolean ok = dogs.remove(ouou);
4.获取List中对象的数量:
dogs.size();
5.清空元素:
dogs.clear();
6.索引元素:
dogs.indexOf(ouou); //不包含此元素时返回-1
dogs.lastIndexOf(feifei); //不包含此元素时返回-1
7.获得或替换对象:
Dog aDog = dogs.get(1); //可能抛出IndexOutOFBoundsException
Dog oldDog = dogs.set(1.ouou); //可能抛出IndexOutOFBoundsException
8.其他方法请查询API文档
List接口小结
方法名 | 说明 |
---|---|
boolean add(Object o) | 在列表的末尾顺序添加元素,起始索引位置从0开始 |
void add(int index,Object o) | 在指定的索引位置添加元素.索引位置必须介于0和列表中元素个数之间 |
int size() | 返回列表中元素的个数 |
Object get(int index) | 返回指定索引位置处的元素. 取出的元素是Object类型,使用前需要进行强制类型转换 |
boolean contains(Object o) | 判断列表中是否存在指定元素 |
boolean remove(Object o) | 从列表中删除元素 |
Object remove(int index) | 从列表中删除指定位置元素,起始索引位置从0开始 |
LinkedList集合类
- 插入,删除操作频繁时,使用ArrayList效率会很低
- 可使用LinkedList来提高效率(LinkedList提供对头部和尾部元素进行添加和删除操作的方法)
使用LinkedList存储元素
LinkedList的特殊方法:
方法名 | 说明 |
---|---|
void addFirst(Object o) | 在列表的首部添加元素 |
vod add last(Object o) | 在列表的末尾添加元素 |
Object getFirst() | 返回列表中的第一个元素 |
Object getLast() | 返回列表中的最后一个元素 |
Object removeFirst() | 删除并返回列表中的第一个元素 |
Object removeLast() | 删除并返回列表中的最后一个元素 |
插入元素对比:
ArrayList的随机访问:
Vector类
另外一个常用的列表类是Vector类,它也实现了List接口,可以实现ArrayList的所有操作。
Vector和ArrayList的异同
- 实现原理、功能相同,可以互用
- 主要区别
Vector线程安全操作相对较慢
ArrayList重速度轻安全,线程非安全
长度需增长时,Vector默认增长一倍,ArrayList增长50%
Vector可以使用capacity()方法获取实际的空间
ArrayList和Vector的默认初始大小均为10
Set类集合
如果反复向List里面添加同一个元素会怎样?
实验证明可以向List当中反复添加同一个元素
如果在程序中,我们不小心向List中添加了重复的元素,可能出现逻辑错误
每次添加之前都要判断List中是已经存在该元素
如果确定元素在列表中不能重复,那么可以使用Set
Set无法随机访问,要访问其元素,只能使用迭代器遍历
❗❗❗**Set使用对象equals()方法的返回值来判断两个对象是不是同一个对象==>如果重写了equals()方法,一定要记得重写hashCode()方法!**❗❗❗
equals()方法注意事项
- 自反性:对于任何值x, x.equals(x) 都应返回 true.
- 对称性:对于任何非空引用值 x 和 y,当且仅当 y.equals(x) 返回 true 时,x.equals(y) 才应返回 true.
- 传递性:对于任何非空引用值 x、y 和 z,如果 x.equals(y) 返回 true,并且 y.equals(z) 返回 true,那么 x.equals(z) 应返回 tru.
- 一致性:对于任何非空引用值 x 和 y,多次调用 x.equals(y) 始终返回 true 或始终返回 false,前提是对象上 equals 比较中所用的信息没有被修改.
- 对于任何值x ,x.equals(null) 都应返回 false.
hashCode()方法的实现
当我们在使用形如HashMap、HashSet、HashTable等以Hash开头的集合类时,hashCode()会被隐式调用以来创建哈希映射关系.
所有用于判断相等的字段在hashCode中都要使用.
hashCode方法的模板:
Set接口
HashSet:保证Set中的元素唯一,但是不保证元素的顺序恒久不变(加入新元素后,元素的遍历顺序可能发生改变)
TreeSet:保证Set中的元素唯一,并且对元素按其自然顺序进行排序
TreeSet特有的操作:E first(); E last(); E ceiling(E e); E floor(E e);E lower(E e); E higher(E e); void poolFirst();void poolLast().
Comparable和Comparator接口
Comparable接口
Comparator接口
狗狗实现了Comparable接口,但是却用intimacy属性来进行比较
即便是狗狗类没有实现过Comparable接口,也只能实现一种比较规则
如何实现依靠两个不同的属性来排序?
排序方法二:使用Comparator接口,可以实现不同的排序标准
Comparator是一个接口
定义了两个方法:
⭕⭕⭕compare()
根据第一个参数小于、等于或大于第二个参数分别返回负整数、零或正整数,通常使用-1, 0, +1.
❌❌❌equals()
!注意!:这是判断比较器本身是否与其他Comparator相等,不是判断TreeSet中的元素
比较对象的注意事项
不管是实现Comprable接口的compareTo()方法或者Comparator接口的compare()方法,都必须遵循如下规则:
对称性:若存在compare(x, y)>0 则 compare(y, x) <0,反之亦然
传递性:((compare(x, y)>0) && (compare(y, z)>0)) 可以推导出compare(x, z)>0
相等替代性:compare(x, y)==0可以推导出compare(x, z)==compare(y, z)
HashSet和TreeSet小结
HashSet | TressSet |
---|---|
保证元素唯一 | 保证元素唯一 |
元素在集合中的顺序不定(并且和放入的顺序无关) | 保证元素排序 |
使用equals()方法和hashCode()方法来保证元素唯一性 | 通过对元素比较判断其结果是否为0来保证元素唯一性(元素可以实现Comparable接口;也可以使用Comparator来比较元素) |
低层数据结构:哈希链表 | 低层数据结构:二叉树 |
算法
能使用TreeSet来对数据进行排序吗?
TreeSet可以在添加数据的时候按一定的规则顺序存放数据
但是TreeSet的特点还是Set,TreeSet的一大特点是,不能存在重复的数据
因此,答案是:否
此外,当TreeSet中的数据改变时,并不会重新排序,对数据的检查仅仅发生在将数据添加到TreeSet时
排序(Sorting)
- 使用Arrays.sort()方法可以对数组进行排序
- 使用Collections.sort()方法可以对集合进行排序
- 排序算法:
当处理原始数据类型时,算法为快速排序
当处理对象类型时,算法为归并排序
乱序(Shuffling) - 乱序就是做和排序相反的工作,将数据随机地打乱顺序
查找(searching) - 从数据中找到需要的数据的位置
Map类集合
Map接口专门处理键值映射数据的存储,可以根据键实现对值的操作:最常用的实现类是HashMap
Map接口常用方法:
方法名 | 说明 |
---|---|
Object put(Object key, Object val) | 以“键-值对”的方式进行存储 |
Object get (Object key) | 根据键返回相关联的值,如果不存在指定的键,返回null |
Object remove (Object key) | 删除由指定的键映射的“键-值对” |
int size() | 返回元素个数 |
Set keySet () | 返回键的集合 |
Collection values () | 返回值的集合 |
boolean containsKey (Object key) | 如果存在由指定的键映射的“键-值对”,返回true |
HashMap和Hashtable的比较
Hashtable和HashMap的异同
- 实现原理、功能相同,可以互用
- 主要区别
Hashtable继承Dictionary类,HashMap实现Map接口
Hashtable线程安全,HashMap线程非安全
Hashtable不允许null值,HashMap允许null值
✍️不涉及到多线程的开发过程中,最好使用ArrayList和HashMap
如果程序中用到多线程,酌情使用Vector和Hashtable
集合遍历与迭代器(Iterator)
如何遍历List,Set和Map集合呢?
方法1:循环(仅适用于List)
方法2:增强型for循环(foreach循环,适用于所有类)
方法3:通过迭代器Iterator实现遍历
- 获取Iterator :Collection 接口的iterate()方法
- Iterator的方法
boolean hasNext(): 判断是否存在另一个可访问的元素
Object next(): 返回要访问的下一个元素
方法4:(仅适用于JDK 1.8) 适用forEach()方法
Map遍历
迭代器Iterator
Set keys=dogMap.keySet(); //取出所有key的集合
Iterator it=keys.iterator(); //获取Iterator对象
while(it.hasNext()){
String key=(String)it.next(); //取出key
Dog dog=(Dog)dogMap.get(key); //根据key取出对应的值
System.out.println(key+"\t"+dog.getStrain());
}
增强for循环
for(元素类型t 元素变量x : 数组或集合对象){
引用了x的java语句
}
两者同样可以遍历其他集合类,通常使用增强型for型循环
从JDK 1.8 之后可以使用forEach方法遍历集合(超纲内容不要求掌握)
…
Set keys=dogMap.keySet();
Keys.forEach(new DogConsumer()); //新建一个匿名对象
class DogConsumer implements Consumer {
public void accept(Object obj) { //每个Set中的对象,都会调用一次该方法
}
}
Lambda表达式
-必要条件:
需要有一个单方法(Single Abstract Method, SAM)接口I
接口必须被标记@FunctionalInterface
- 通过表达式
泛型集合
JDK5.0开始,Java使用泛型改写了集合框架中的所有接口和类.
可以在申明集合的同时确定该集合所能接受的类的类型.
从集合中取出元素时,也只能取出规定的类型.
泛型类
当一个类需要处理不知类型的数据时,可以在定义时加上泛型,用大写字母代替任意类型:
泛型方法
当一个方法不知道内部需要处理的类型时,可以申明成泛型方法,方法的参数,返回值和局部变量都可以使用泛型类型.
自动装箱和拆箱