1. 集合概述
由于我们开发过程中,难以避免不去操作数据,操作对象就有可能对数据进行存储,存储数据的方式多种多样,之前我们都是将数据存储在数组中,但数组的特点是长度固定不可改变的,这样就不适合用于存储变化(个数的变化)的数据。因此,Java提供一种容器用于存储数据,这个容器就叫做集合。
2. 集合和数组的区别
数组:
- 长度固定
- 可以存储基本数据类型,也可以存储引用数据类型
- 存储的数据类型必须要一致(比如说创建一个int类型的数组,它就只能存整数,不能存double或者String类型等)
集合:
- 长度可变
- 只能存储引用数据类型
- 可以存储不同类型的对象。(可以存int、String、double等)
3. 集合的体系:
![189ed10ecc39265ce269c55a4a6b20a8.png](https://i-blog.csdnimg.cn/blog_migrate/21dd43a70b4b822606bc035f138a6ccc.jpeg)
4.下面看一下各成员特点
|- - List: 有序(序指的是存和取得顺序一致,怎么样存就怎么样取),元素可以重复
|- - - - - - - ArrayList: 底层数据结构是数组,查询快,增删慢,线程不安全,效率高
|- - - - - - - Vector: 底层数据结构是数组,查询快,增删慢,线程安全,效率低
|- - - - - - - LinkedList: 底层数据结构是链表,查询慢,增删快,线程不安全,效率高
|- - Set: 无序,元素不可以重复
|- - - - - - - HashSet: 底层数据结构是哈希表,线程不安全,效率高
|- - - - - - - TreeSet: 底层数据结构是二叉树,线程不安全,效率高
5. Collection接口中的常用方法
![6c104e3539c92ebd9aad12f2298b62cc.png](https://i-blog.csdnimg.cn/blog_migrate/7eb5fdbd6fe76e9e492b559618b36e77.jpeg)
注:Collection是一个根接口,继承它的子类或者子接口都能使用它的方法
6. List集合特有的方法
![0269a5010e69333c58b82b75357aeaf6.png](https://i-blog.csdnimg.cn/blog_migrate/4ed5694c5de3fcb73830a314d621f310.jpeg)
7. ArrayList
ArrayList是List接口下的一个实现类之一,也是最常用的一个集合,由于它的底层是数组结构实现的,因此可以看成是一个长度可变的数组,下面介绍关于ArrayList的常见使用
public class Demo { public static void main(String[] args) { //定义一个ArrayList集合 ArrayList arrayList = new ArrayList(); arrayList.add("你好"); //添加元素 arrayList.add(20); arrayList.add(20); //添加了重复元素 arrayList.set(0, "hello"); //修改了索引为0的元素 int size = arrayList.size(); //获取集合的大小 System.out.println("集合的大小为:"+size); //输出:集合的大小为:3 System.out.println(arrayList.get(2)); //输出:20 System.out.println(arrayList); //输出:[hello, 20, 20] } }
7.1 集合的遍历
7.1.1.通过集合转数组,然后再对数组进行遍历
Collection c = new ArrayList(); c.add("abc"); c.add("abcd"); c.add("abcde"); Object[] array = c.toArray(); //将集合转为数组 for(int i=0; i
7.1.2.通过get()和size()方法结合(仅仅适用于Collection的子接口List接口)
//定义一个ArrayList集合 ArrayList arrayList = new ArrayList(); arrayList.add(10); arrayList.add(50); arrayList.add("你好"); arrayList.add(8); for (int i = 0; i < arrayList.size(); i++) { Object obj = arrayList.get(i); //根据索引获取值,在此返回的是Object数据类型 System.out.print(obj + " "); }
7.1.3.通过迭代器实现(适用于所有集合) (下面将会介绍)
//定义一个ArrayList集合 ArrayList arrayList = new ArrayList(); arrayList.add(10); arrayList.add(50); arrayList.add("你好"); arrayList.add(8); //第一步:创建迭代器对象 Iterator iterator = arrayList.iterator(); //第二步:通过hasNext()方法判断迭代器中是否有元素 while (iterator.hasNext()) { //第三步:通过next()方法获取元素 Object object = iterator.next(); System.out.print(object + ""); }}
7.1.4.通过增强for循环遍历(适用于所有集合和数组) (推荐使用)
注意:增强for只能操作集合和数组
定义格式:
for(数据类型 临时变量名 : 集合或者数组){
}
例如定义了一个集合ArrayList list = new ArrayList(); 此处的集合如不指定数据类型,默认为Object类型,则它的增强for格式就为:
for(Object obj:list) {
}
//定义一个ArrayList集合 ArrayList arrayList = new ArrayList(); arrayList.add(10); arrayList.add(50); arrayList.add("你好"); arrayList.add(8); //增强for进行遍历 for (Object object : arrayList) { System.out.print(object + " "); }
7.2 迭代器
7.2.1 何为迭代器
迭代器是从集合中取出元素的一种标准方式,我们取出任何集合中的元素都可以按照这个标准来获取,主要遍历Collection集合中的元素。迭代是取出集合中元素的一种方式
7.2.2 原理
因为多种集合的数据结构不同,所以存储方式不同,因此取出方式也不同。这个时候,我们就把判断和获取功能定义在了一个接口中,将来,遍历哪种集合的时候,只要该集合内部实现这个接口即可(由于集合内部已经实现了该接口,因此直接使用即可,无需我们手动实现 )
7.2.3 使用方式
通过集合对象调用iterator()方法便可获得一个且迭代器对象Iterator,然后再通过hasNext()方法判断是否含有元素,最后通过next()方法获取集合中的元素。(使用示例见上文7.1集合遍历中的第三种方法)
7.2.4 常见问题
在我们对集合进行遍历的时候,有时候可能会对集合进行删除或者增加元素。但当我们使用Iterator对集合进行遍历的同时进行增删操作时,会出现一个异常,那就是并发修改异常 ConcurrentModificationException。 但要是我们硬是要在迭代的时候对元素进行增删操作的时候该肿么办呢?这时就有一个办法解决啦!这个办法就是通过集合对象获取Iterator的子接口ListIterator (例如:ListIterator iterator = arrayList.listIterator(); )
7.2.5 Iterator 和 ListIterator的区别
1、Iterator是ListIterator的父接口
2、Iterator是Collection集合的公共的取出容器中元素的方式,对于List,Set都通用。而ListIterator是List集合的特有的取出元素的方式
3、Iterator中只具备hasNext(),next(),remove()方法,可以删除被遍历到的元素ListIterator中具备对遍历到的元素进行增(add)删(remove)改(set)查(next)的方法,可以对元素逆向遍历previouse相对于next,hasPreviouse相对于hasNext(用的很少)
7.2.6 Iterator 和 ListIterator的成员方法
![a864dd17cde60bd74103e5c5baffcc30.png](https://i-blog.csdnimg.cn/blog_migrate/08c389f394c0ecd8ca738deddb7ee842.jpeg)
8. LinkedList
LinkedList底层实现是链式结构,它的特点是增加删除元素快,查询元素慢。就像人们手牵手连在一起排成一条队列,每个人就可以看做一个元素,当有一个人想进入或者退出队列的时候,只是旁边两人断开手就行了,要是想查询某人处于什么位置,只能从开头或者默认一个一个地查询下去,这样效率就大大下降了。
8.1 特有的方法
![434cd901bdc0076acb18fc0912725a7d.png](https://i-blog.csdnimg.cn/blog_migrate/93e8fecc0f0ea1253ecf0bfbc823e4ea.jpeg)
8.2 使用示例
import java.util.LinkedList;public class LinkedListDemo { public static void main(String[] args) { LinkedList list = new LinkedList(); list.addFirst("java01"); list.addFirst("java02"); list.addFirst("java03"); list.addFirst("java04"); System.out.println(list); System.out.println(list.getFirst()); while (!list.isEmpty()) { System.out.println(list.removeLast()); } }}
9. 泛型
9.1 何为泛型
泛型是一种把明确类型的工作放在了创建对象或者调用方法时候才去明确的特殊的类型,泛型就相当于一个预定义类型
9.2 好处
把系统运行期间的异常提前到了编译期间,提高了安全性优化了程序设计,不需要再做强壮了
9.3 缺点
泛型只能传入引用类型,不能传入基本类型,如需要传入基本类型只能传入基本类型对应的包装类类型
包装类表如下:
![7dead7a3893856a8a020741991650260.png](https://i-blog.csdnimg.cn/blog_migrate/db8495dc8f9af54bd4e820bb0bb34052.jpeg)
9.4 泛型的使用
泛型可以定义在类中,方法中和接口中。下面介绍在创建对象时使用泛型和不使用泛型的区别。
不使用泛型时:
//定义一个ArrayList集合,没有指定泛型,默认为Object类型 ArrayList arrayList = new ArrayList(); arrayList.add("元素1"); arrayList.add("元素2"); arrayList.add("元素3"); for (int i = 0; i < arrayList.size(); i++) { //由于ArrayList默认为Object类型,因此在不加泛型的时候获取元素需要强转 String string = (String) arrayList.get(i); System.out.print(string + " "); }
使用泛型:
//定义一个ArrayList集合,指定泛型为String类型 ArrayList arrayList = new ArrayList(); arrayList.add("元素1"); arrayList.add("元素2"); arrayList.add("元素3"); //arrayList.add(2); 这里如果添加非String类型,会报错,因为已经指定泛型,从而将运行期间的异常提前到了编译期间 for (int i = 0; i < arrayList.size(); i++) { //由于在创建集合对象时已经给定泛型,因此在此不需要强转 String string = arrayList.get(i); System.out.print(string + " "); }
10. ArrayList练习
需求:ArrayList去除集合中字符串的重复值(字符串的内容相同)
import java.util.ArrayList;import java.util.Iterator;/** * 思路:想要对集合中的元素进去重,可以创建一个新的集合, 然后再遍历的时候判断新集合是否包含某个元素。 如果有了就不添加,否则就添加到新集合里面,最终得到的新集合就是没有重复元素的集合。 */ public class Demo { public static void main(String[] args) { //定义一个ArrayList集合 ArrayList arrayList = new ArrayList(); //往集合中添加数据 arrayList.add("aa"); arrayList.add("cc"); arrayList.add("aa"); arrayList.add("bb"); arrayList.add("aa"); Iterator iterator = arrayList.iterator(); //获取迭代器 ArrayList newList = new ArrayList<>(); //创建一个新的集合 while (iterator.hasNext()) { String string = iterator.next(); //获取就集合中的每个元素 if (!newList.contains(string)) { //如果新集合不包含该元素,就添加到新集合中 newList.add(string); } } System.out.println(newList); //输出结果:[aa, cc, bb] }}
11. HashSet集合
HashSet集合存储的元素不会重复,并且存储顺序是不一致的。那么HashSet集合在添加元素时是如何保证元素不会重复添加呢?其实它是通过两个方法来进行判断的。
11.1 保证不重复添加元素
- hashCode()方法:首先通过调用对象的hashCode() 方法,返回一个int型的数据,这个数据就是哈希值。当存储两个元素时,会判断这两个元素的哈希值是否相同,如果不相同,就添加到集合中;否则会进行调用equals()方法
- equals()方法:当两个元素的元素哈希值相等时,会调用此方法来判断两者的内容是否相同。不同则添加,相同就不添加到集合中
注意: 如果两个元素的哈希值不同,就不再需要调用equals方法了。
public class Demo { public static void main(String[] args) { //1.创建一个HashSet集合,并指定为String泛型 HashSet hashSet = new HashSet<>(); //往集合中添加元素 hashSet.add("aa"); hashSet.add("bb"); hashSet.add("aa"); //添加重复元素 hashSet.add("cc"); //获取hashSet的迭代器对象 Iterator iterator = hashSet.iterator(); //对元素进行遍历 while (iterator.hasNext()) { String string = iterator.next(); System.out.print(string + " "); //输出:aa bb cc } }}
11.2 HashSet主要方法
通过API可以看出HashSet的主要方法有:
![8f410ab5bfaf8ef54047967c0d7879e8.png](https://i-blog.csdnimg.cn/blog_migrate/ba9c877fc2162c7b790e2de6d0ecec0e.jpeg)
11.3 HashSet存储自定义对象
- 需求:往HashSet集合中存储Person对象,如果姓名和年龄相同,视为同一个人。
首先定义一个Person实体类
/** * 定义一个Person实体类(这里面的方法都是同快捷键生成的 Alt+Shift+S) * 必须重写hashCode()方法和equals()方法,不然在添加对象进集合的时候不会去重 */ public class Person { /** 姓名*/ private String name; /** 年龄*/ private int age; /** 无参构造方法*/ public Person() { super(); } /** 有参构造方法*/ public Person(String name, int age) { super(); this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } /** * 重写hashCode()方法 */ @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + age; result = prime * result + ((name == null) ? 0 : name.hashCode()); return result; } /** * 重写equals()方法 */ @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; Person other = (Person) obj; if (age != other.age) return false; if (name == null) { if (other.name != null) return false; } else if (!name.equals(other.name)) return false; return true; } }
然后再创建一个集合存储元素,并遍历
public class HashSetTest { public static void main(String[] args) { //创建一个集合,并只能存储Person对象 HashSet hs = new HashSet<>(); //往集合中添加了6个元素,有重复 hs.add(new Person("张三