java进阶6——集合
程序员文章站
2022-05-04 09:56:47
...
集合&迭代器
集合体系结构
- 集合体系图
- 集合的体系是如何形成的
由于不同的数据结构(数据的组织,存储方式),所以java为我们提供了不同的集合,但不同的数据结构中又有许多类似的功能,所以将这些类似的功能向上提取,最终形成集合的体系
Collection中的常用功能
boolean add(Object e); // 向集合中添加元素
void clear(); // 清空集合中所有元素
boolean contains(Object o); // 判断集合中是否包含某个元素
boolean isEmpty(); // 判断集合中的元素是否为空
boolean remove(Object o); // 根据元素的内容来删除某个元素
int size(); // 获取集合的长度
Object[] toArray(); // 能够将集合转换成数组并把集合中的元素存储到数组中
迭代器
java中提供了很多个集合,它们在存储元素时,采用的存储方式不同。我们要取出这些集合中的元素,可通过一种通用的获取方式来完成。
Collection集合元素的通用获取方式:在取元素之前先要判断集合中有没有元素,如果有,就把这个元素取出来,继续在判断,如果还有就再取出出来。一直把集合中的所有元素全部取出
。这种取出方式专业术语称为迭代。
集合中把这种取元素的方式描述在Iterator接口中。
- Iterator接口的常用方法如下
Iterator 集合对象.iterator(); // 返回一个Iterator的对象
hasNext(); // 判断集合中是否有元素可以迭代
next(); // 用来返回迭代的下一个元素,并把指针向后移动一位
- 使用迭代器的注意事项:
使用迭代器会碰到一个异常Exception in thread "main" java.util.ConcurrentModificationException:并发修改异常
这是因为在使用迭代器时,我们直接通过集合进行添加,而迭代器的原理其实就是一个集合的副本,当集合中的元素与迭代器中的元素个数或值不同时,就会发生并发修改异常,解决办法有两种,第一种不使用迭代器进行遍历,第二种使用迭代器的子类对象,如果要修改集合,直接通过迭代器去修改副本中的数据,修改之后,副本会同步集合中的数据
增强for&泛型
泛型
- 泛型的引入
由于集合可以存储任意类型的对象,当我们存储了不同类型的对象,就有可能在转换的时候出现类型转换异常,所以java为了解决这个问题,给我们提供了一种机制,叫做泛型 - 泛型的使用
当类上定义<>的时候就可以使用泛型,例如ArrayList类的定义:
class ArrayList,那么我们在创建ArrayList对象的时候就可以指定<>中E的类型
ArrayList al=new ArrayList(),那么String就把E替换掉了这里的E可以替换为T或?,都表示泛型
增强for
增强for循环是JDK1.5以后出来的一个高级for循环,专门用来遍历数组和集合的。它的内部原理其实是个Iterator迭代器,所以在遍历的过程中,不能对集合中的元素进行增删操作
。
- 格式:
for(元素的数据类型 变量 : Collection集合or数组的对象){
}
常见数据结构
数组
- 采用该结构的集合,对元素的存取有如下的特点:
查找元素快:通过索引,可以快速访问指定位置的元素
增删元素慢 ,每次添加元素需要移动大量元素或这创建新的数组
链表
- 采用该结构的集合,对元素的存取有如下的特点:
A:多个节点之间,通过地址进行连接。例如,多个人手拉手,每个人使用自己的右手拉住下个人的左手,依次类推,这样多个人就连在一起了。
B:查找元素慢:想查找某个元素,需要通过连接的节点,依次向后查找指定元素
C:增删元素快:
增加元素:只需要修改连接下个元素的地址即可。
删除元素:只需要修改连接下个元素的地址即可
- 使用的选择
由上述两个的特点可以得出一下结论:
如果查询较多,则使用数组结构的集合
如果增删较多,则使用链表结构的集合
栈&队列
- 堆栈,采用该结构的集合,对元素的存取有如下的特点:
先进后出(即,存进去的元素,要在后它后面的元素依次取出后,才能取出该元素)。例如,子弹压进弹夹,先压进去的子弹在下面,后压进去的子弹在上面,当开枪时,先弹出上面的子弹,然后才能弹出下面的子弹。 - 队列,采用该结构的集合,对元素的存取有如下的特点:
先进先出(即,存进去的元素,要在后它前面的元素依次取出后,才能取出该元素)。例如,安检。排成一列,每个人依次检查,只有前面的人全部检查完毕后,才能排到当前的人进行检查。
List体系
List体系特点
A:有序的(存储和读取的顺序是一致的)
B:有整数索引 C:允许重复的
List的特有功能
void add(int index, E element); // 将元素添加到index索引位置上
E get(int index); // 根据index索引获取元素
E remove(int index); // 根据index索引删除元素
E set(int index, E element); // 将index索引位置的的元素设置为element
LinkedList特有功能
LinkedList底层使用的是链表结构,因此增删快,查询相对ArrayList较慢
void addFirst(E e); // 向链表的头部添加元素
void addLast(E e); // 向链表的尾部添加元素
E getFirst(); // 获取链头的元素,不删除元素
E getLast(); // 获取链尾的元素,不删除元素
E removeFirst(); // 返回链头的元素并删除链头的元素
E removeLast(); // 返回链尾的元素并删除链尾的元素
Set体系
Set体系的特点(与List体系的三个特点都相反)
A:存入集合的顺序和取出集合的顺序不一致
B:没有索引 C:存入集合的元素没有重复
HashSet集合
- HashSet唯一性原理
规则:新添加到HashSet集合的元素都会与集合中已有的元素一一比较
首先比较哈希值(每个元素都会调用hashCode()产生一个哈希值)
如果新添加的元素与集合中已有的元素的哈希值都不同,新添加的元素存入集合
如果新添加的元素与集合中已有的某个元素哈希值相同,此时还需要调用equals(Object obj)比较
如果equals(Object obj)方法返回true,说明新添加的元素与集合中已有的某个元素的属性值相同,那么新添加的元素不存入集合
如果equals(Object obj)方法返回false, 说明新添加的元素与集合中已有的元素的属性值都不同, 那么新添加的元素存入集合
下面这个例子很重要
public class Demo {
public static void main(String[] args) {
HashSet<Person> hashSet = new HashSet<Person>();
Person p1 = new Person(12,"zhangsan");
Person p2 = new Person(12,"san");
Person p3 = new Person(13,"san");
hashSet.add(p1);
System.out.println("////////////////////");
hashSet.add(p2);
System.out.println("////////////////////");
hashSet.add(p3);
System.out.println("////////////////////");
for (Person person : hashSet) {
System.out.println(person);
}
}
}
package com.bsw.hashsetDemo;
public class Person {
int age;
String name;
public Person(int age, String name) {
super();
this.age = age;
this.name = name;
}
@Override
public String toString() {
return "Person [age=" + age + ", name=" + name + "]";
}
@Override
public int hashCode() {
return 1;
}
@Override
public boolean equals(Object obj) {
Person person = (Person)(obj);
System.out.println(this.name + "---------------" + person.name);
if (!name.equals(person.name)) {
return false;
}
if (age != person.age) {
return false;
}
return true;
}
}
这张图是上述案例输出的结果
这个结果是因为源码中下图的方法进行判断,如果比较两个值得key不相同的话,就不会执行equals方法,如果key相同,则会执行双与后的equals方法
- hashCode方法优化:
如果让hashCode()方法返回一个固定值,那么每个新添加的元素都要调用equals(Object obj)方法比较,那么效率较低
只需要让不同属性的值的元素产生不同的哈希值,那么就可以不再调用equals方法比较提高效率
Collections中的方法
static void swap(List list, int i, int j); // 将指定列表中的两个索引进行位置互换
// 例子:Collections.swap(集合对象, 索引1, 索引2);
static void sort(List<T> list); // 按照列表中元素的自然顺序进行排序
// 例子:Collections.sort(集合对象);
static void shuffle(List list); // 是否,随机置换
// 例子:Collections.shuffle(集合对象);
static void reverse(List list); // 反转
// 例子:Collections.reverse(集合对象);
static void fill(List list, Object obj); // 使用指定的对象填充指定列表的所有元素
// 例子:Collections.fill(集合对象, 指定的填充元素);
static void copy(List dest, List src); // 是把源列表中的数据覆盖到目标列表
// 例子:Collections.copy(目标集合对象, 源集合对象);这里需要注意,目标集合长度大于等于源集合对象
static int binarySearch(List list, Object key); // 使用二分查找法查找指定元素在指定列表的索引位置
// 例子:Collections.binarySearch(集合对象, 指定索引);
面试题:Collection和Collections有什么区别?
Collection是集合体系的最顶层,包含了集合体系的共性
Collections是一个工具类,方法都是用于操作Collection
Map接口概述
Map集合与Collection集合的区别
- Collection集合中的所有元素都是孤立的,可以理解为单身状态
- Map集合中的所有元素是以键值对存储的,
一个键对应一个值,键不可重复,但是值可以重复
,可以理解为夫妻状态 - 因此,通常将Collection的集合称为
单列集合
,Map的集合称为多列集合
Map常用功能
A:映射功能(添加):
V put(K key, V value); // 以键=值的方式存入Map集合,如果key存在,则覆盖value,并将原来的value返回
B:获取功能:
V get(Object key); // 根据键获取值
int size(); // 返回Map中键值对的个数
C:判断功能:
boolean containsKey(Object key); // 判断Map集合中是否包含键为key的键值对
boolean containsValue(Object value); // 判断Map集合中是否包含值为value键值对
boolean isEmpty(); // 判断Map集合中是否没有任何键值对
D:删除功能:
void clear(); // 清空Map集合中所有的键值对
V remove(Object key); // 根据键值删除Map中键值对
E:遍历功能:
Set<Map.Entry<K,V>> entrySet(); // 将每个键值对封装到一个个Entry对象中,再把所有Entry的对象封装到Set集合中返回
Set<K> keySet(); // 将Map中所有的键装到Set集合中返回
Collection<V> values(); // 返回集合中所有的value的值的集合
Map的两种遍历方式
- 通过keySet()方法,获取所有key值,遍历key得到对应的value
- 通过entrySet()方法,获取一个装有多个Entry对象的Set集合,遍历集合,通过entry对象获取对应的key和value