Collection接口继承体系图
Collection接口常用方法
返回值类型 | 方法 | 解释 |
---|---|---|
boolean | add(E e) | 向列表的尾部添加指定的元素(可选操作) |
boolean | addAll(Collection<? extends E> c) | 将指定 collection 中的所有元素都添加到此 collection 中(可选操作) |
void | clear() | 移除此 collection 中的所有元素(可选操作) |
boolean | contains(Object o) | 判断集合是否包含此元素,包含返回true |
boolean | equals(Object o) | 比较此 collection 与指定对象是否相等 |
int | hashCode() | 返回此 collection 的哈希码值 |
boolean | isEmpty() | 如果此 collection 不包含元素,则返回 true |
Iterator<E> | iterator() | 返回在此 collection 的元素上进行迭代的迭代器 |
boolean | remove(Object o) | 删除第一次出现在集合的指定元素,返回值为被删除的元素(如果存在) |
int | size() | 返回列表中的元素数 |
Object[] | toArray() | 返回按适当顺序包含列表中的所有元素的数组(不常用) |
一、List集合
List 就是列表的意思,它是Collection 的一种,即继承了 Collection 接口,以定义一个允许重复项的有序集合。该接口不但能够对列表的一部分进行处理,还添加了面向位置(索引)的操作。List 是按对象的进入顺序进行保存对象,而不做排序或编辑操作。它除了拥有Collection接口的所有的方法外还拥有一些其他的方法。
面向位置(索引)的操作包括插入某个元素或 Collection 的功能,还包括获取、除去或更改元素的功能。在 List 中搜索元素可以从列表的头部或尾部开始,如果找到元素,还将报告元素所在的位置。
List接口子实现类的特点
- ArrayList:底层结构是数组,增删慢,查询快(线程不安全,效率高)
- Vector:底层结构是数组,增删,查询都很慢(线程安全,因此查询效率比ArrayList要低)
- LinkedList:内部是链表数据结构,,增删元素的速度很快(线程不安全)
List集合特有的常用方法
返回值类型 | 方法 | 解释 |
---|---|---|
boolean | addAll(Collection<? extends E> c) | 按照迭代器返回的元素顺序,将指定集合插入到指定集合中 |
boolean | addAll(int index, Collection<? extends E> c) | 从指定的位置开始,将指定 collection 中的所有元素插入到此列表中 |
E | get(int index) | 返回列表中指定位置的元素 |
int | indexOf(Object o) | 返回该元素第一次出现在集合中的索引值;若不包含该元素,则返回 -1 |
int | lastIndexOf(Object o) | 返回该元素最后一次出现在集合中的索引值;若不包含该元素,则返回 -1 |
ListIterator<E> | listIterator() | 返回此列表元素的列表迭代器(按适当顺序) |
E | remove(int index) | 根据索引删除元素,返回值为被删除的元素 |
E | set(int index, E element) | 根据索引,修改集合中的元素,返回值为替换前的元素 |
常用方法演示
public void test01() {
ArrayList<String> list = new ArrayList<>();
list.add("三国演义");
list.add("西游记");
list.add("水浒传");
list.add("红楼梦");
Iterator<String> ite = list.iterator();
while (ite.hasNext()) {
String next = ite.next();
// 判断当前元素是否是"西游记"
if (next.equals("西游记")) {
// 根据索引修改当前元素为 "吴承恩"
list.set(list.indexOf(next), "吴承恩");
}
// 输出结果:三国演义 西游记 水浒传 红楼梦
System.out.print(next + "\t");
}
System.out.println();
// 将集合list转换成Object数组
Object[] arr = list.toArray();
for (int i = 0; i < arr.length; i++) {
String str = (String) arr[i];
// 输出结果:三国演义 吴承恩 水浒传 红楼梦
System.out.print(str + "\t");
}
}
注意:集合中的元素必须是对象,不能是基本数据类型(JDK 1.5后提供自动拆装箱)
Collection集合的remove(obj)方法和Iterator接口中的remove()方法的使用
public void test02() {
List<String> list1 = new ArrayList<>();
List<String> list2 = new ArrayList<>();
list1.add("三国演义");
list1.add("西游记");
list1.add("水浒传");
list1.add("红楼梦");
Iterator<String> ite = list1.iterator();
while (ite.hasNext()) {
String next = ite.next();
// 判断当前元素是否是"西游记"
if (next.equals("西游记")) {
// 移除当前元素"西游记"
//list.remove(next); //报错,并发修改异常ConcurrentModificationException
// 使用Iterator的方法移除元素不会报错
ite.remove();
}
}
list2.addAll(list1);
System.out.println("list1 = " + list1);// list1 = [三国演义, 水浒传, 红楼梦]
System.out.println("list2 = " + list2);// list2 = [三国演义, 水浒传, 红楼梦]
}
注意:迭代器遍历集合时,使用Collection集合的remove(obj)方法会报并发修改异常java.util.ConcurrentModificationException,
但使用Iterator接口中的remove()方法,程序运行正常。
出现异常 java.util.ConcurrentModificationException的原因及解决办法?
原因:在迭代过程中,使用了List集合的方法对元素进行操作。导致迭代器并不知道集合中的变化,容易引发数据的不确定性
解决办法:
- 在迭代时,不要使用集合的方法操作元素
- 使用普通的for循环遍历集合
- 使用ListIterator迭代器操作元素,解决了使用Iterator迭代过程中可能会发生的错误情况(后面会讲)
1.1、ArrayList集合特点及方法
特点:数据存储的结构是数组结构。元素增删慢,查找快,由于日常开发中使用最多的功能为查询数据、遍历数据,所以ArrayList是最常用的集合。
常用方法:全部继承自List接口
1.2、LinkedList集合的特点及方法
特点:数据存储的结构是链表结构。元素增删快,查找慢。实际开发中对一个集合元素的添加与删除经常涉及到首尾操作,而LinkedList提供了大量首尾操作的方法
常用方法:
返回值类型 | 方法 | 解释 |
---|---|---|
void | addFirst(E e) | 将指定元素插入此列表的开头 |
void | addLast(E e) | 将指定元素添加到此列表的结尾 |
E | getFirst() | 返回此列表的第一个元素(链表为空,抛出NoSuchElementException) |
E | getLast() | 返回此列表的最后一个元素 |
E | removeFirst() | 移除并返回此列表的第一个元素 |
E | removeLast() | 移除并返回此列表的最后一个元素 |
E | peekFirst() | 获取但不移除此列表的第一个元素(链表为空,返回null) |
E | pollFirst() | 获取但不移除此列表的第一个元素(列表为空,返回 null) |
利用LinkedList集合特点模拟一个堆栈或者队列数据结构
堆栈:先进后出 First In Last Out
队列:先进先出 First In First Out
/**
* 利用LinkedList集合特点模拟一个堆栈
* 堆栈:先进后出 First In Last Out
*/
@Test
public void test05() {
LinkedList<String> lklist = new LinkedList<>();
lklist.addLast("one");
lklist.addLast("two");
lklist.addLast("three");
lklist.addLast("four");
lklist.addLast("five");
Iterator<String> ite = lklist.iterator();
while (ite.hasNext()) {
String next = ite.next();
System.out.print(next + " ");// one two three four five
}
}
/**
* 利用LinkedList集合特点模拟队列数据结构
* 队列:先进先出 First In First Out
*/
@Test
public void test06() {
LinkedList<String> lklist = new LinkedList<>();
lklist.addFirst("one");
lklist.addFirst("two");
lklist.addFirst("three");
lklist.addFirst("four");
lklist.addFirst("five");
Iterator<String> ite = lklist.iterator();
while (ite.hasNext()) {
String next = ite.next();
System.out.print(next + " "); // five four three two one
}
}
1.3、Vector集合的特点及方法(被ArrayList取代)
特点:数据存储的结构是数组结构,为JDK中最早提供的集合。Vector集合已被ArrayList替代。枚举Enumeration已被迭代器Iterator替代。
常用方法:
返回值类型 | 方法 | 解释 |
---|---|---|
void | addElement(E obj) | 将指定的组件添加到此向量的末尾,将其大小增加 1 |
E | elementAt(int index) | 返回指定索引处的组件 |
Enumeration<E> | elements() | 返回此向量的组件的枚举 |
Vector集合对ArrayList集合使用的对比
二、Set接口
Java 中的Set和正好和数学上直观的集(set)的概念是相同的。Set最大的特性就是不允许在其中存放的元素是重复的。根据这个特点,我们就可以使用Set 这个接口来实现前面提到的关于商品种类的存储需求。Set 可以被用来过滤在其他集合中存放的元素,从而得到一个没有包含重复新的集合。
Set接口中的方法和Collection一致,这里就不列出了
Set接口子实现类的特点
- HashSet:内部数据结构是哈希表(线程不安全)
- LinkHashSet:内部数据结构是链表和哈希表(线程不安全)
- TreeSet:内部数据结构是平衡树(Balanced tree),元素唯一、有序(线程不安全)
重写hashCode()、equals()方法,保证Set集合元素唯一性,源码判断步骤如下
①判断的是两个元素的哈希值是否相同,即hashCode()
如果哈希值不同,不需要判断equals(),就可以保证元素的唯一了
如果相同,再判断两个对象的内容是否相同,即equals()
注意:hashCode()方法判断对象的哈希值是否相同;equals()方法判断内容是否相同
注意:HashSet存储JavaAPI中提供的类型元素时,不需要重写元素的hashCode()和equals()方法,因为这两个方法,在JavaAPI的每个类中已经重写,如String类、Integer类等,但是自定义类需要自己重写hashCode()和equals()方法,否则无法保证元素唯一性。
下面代码体现了Set集合的唯一性
/**
* 利用Set集合元素唯一性移除List集合中重复元素
* String类已经重写hashCode()和equals()方法
*/
@Test
public void test03() {
Collection<String> set = new HashSet<>();
Collection<String> list = new ArrayList<>();
for (int i = 5; i > 0; i--) {
list.add(i + "");
}
list.add("1");
list.add("3");
list.add("5");
System.out.println("list = " + list);// 输出结果:list = [5, 4, 3, 2, 1, 1, 3, 5]
set.addAll(list);
System.out.println("set = " + set);// 输出结果:set = [1, 2, 3, 4, 5]
}
疑惑:set = [1, 2, 3, 4, 5]的输出结果有点疑惑,set集合自动将结果排序了,查看了下Integer类实现了Comparable接口
2.1、HashSet的特点及方法
特点:数据存储的结构是哈希表。元素存取无序,集合元素唯一,元素并不是按照存入时的顺序(和List显然不同) 是按照哈希值确定存放在HashSet集合的位置,因此也是按照哈希值取数据的
常用方法:全部实现自Set接口
2.2、LinkHashSet的特点及方法
特点:链表和哈希表组合的一个数据存储结构,因此元素存取有序、且保证元素唯一性
常用方法:全部实现自Set接口
public void test07() {
Set<String> set = new LinkedHashSet<String>();
set.add("三国演义");
set.add("西游记");
set.add("水浒传");
set.add("红楼梦");
set.add("西游记");
set.add("三国演义");
Iterator it = set.iterator();
while (it.hasNext()) {
System.out.print(it.next() + "\t");// 三国演义 西游记 水浒传 红楼梦
}
}
2.3、TreeSet的特点及方法
特点:TreeSet集合的底层是二叉树进行排序的,集合元素唯一性
常用方法:
TreeSet判断元素唯一性的方式:根据比较方法的返回结果判断,是0,就是相同元素;不是0,元素不相同
TreeSet对元素进行排序的方式
- 元素自身具备比较性:元素实现Comparable接口,重写compareTo方法
- 容器具备比较性:使用带参构造创建TreeSet(Collection<? extends E> c) ,实现compare(T o1, T o2)方法
注意:当Comparable比较方式和Comparator比较方式同时存在时,以Comparator的比较方式为主。
/**
* 验证TreeSet集合的唯一、排序
*/
@Test
public void test08() {
TreeSet<String> ts = new TreeSet<>();
ts.add("5");
ts.add("2");
ts.add("3");
ts.add("4");
ts.add("1");
ts.add("5");
Iterator<String> ite = ts.iterator();
while (ite.hasNext()) {
Object next = ite.next();
System.out.print(next + " ");// 输出结果:1 2 3 4 5
}
}
三、总结(集合框架中常用类比较)
看到array,就要想到角标
看到link,就要想到first,last
看到hash,就要想到hashCode,equals
看到tree,就要想到两个接口Comparable,Comparator