集合
苟有恒,何必三更眠五更起; 最无益,莫过一日曝十日寒。
集合(Collection)
集合的长度是可变的,是用来存储多个同类型的容器
集合的顶层都是接口, 其中Collection接口是单列集合的顶层接口, Map接口是双列集合的顶层接口
体系图
我们采用学顶层,用底层的方式进行学习
解释: 因为顶层封装的是整个继承体系的共性内容, 而底层才是具体的实现, 体现
(子类继承父类,父类有所有的方法,用接口的形式进行调用比较方便.)
集合有两大子体系:
list体系特点:有序,可重复
set体系的特点:无序,唯一
一、迭代器<iterator>
public Iterator<E> iterator() //根据集合对象, 获取其对应的迭代器对象.
解释:
因为Iterator是接口, 所以这里返回的其实是Iterator接口的子类对象.
迭代器是依赖于集合而存在的.
Iterator迭代器中的方法
public boolean hasNext() //判断迭代器中是否还有下一个元素.
public E next() 获取迭代器中的下一个元素.
案例一
通过列表迭代器进行逆向遍历之前, 必须先进行一次正向遍历.
创建List集合, 用来存储字符串.
往List集合中添加3个字符串值, 分别是: “hello”, “world”, “java”.
通过列表迭代器对List集合分别进行正向遍历和逆向遍历.
/*
ListIterator简介
概述:
它是Iterator迭代器的子类(接口), 它主要是作用于List体系的, 它叫: 列表迭代器.
涉及到的成员方法:
List集合中的成员方法
public ListIterator<E> listIterator(); //根据集合对象获取其对应的列表迭代器对象.
ListIterator中的成员方法:
hasNext(); 判断集合中是否有下一个元素.
next(); 有就获取集合中的下一个元素.
hasPrevious(); 判断集合中是否有上一个元素.
previous(); 有就获取集合中的上一个元素.
细节:
通过列表迭代器进行逆向遍历之前, 必须先进行一次正向遍历.
*/
public class Demo01 {
public static void main(String[] args) {
//1.创建List集合, 用来存储字符串.
List<String> list = new ArrayList<>();
//2.往List集合中添加3个字符串值, 分别是: "hello", "world", "java".
list.add("hello");
list.add("world");
list.add("java");
//3.通过列表迭代器对List集合分别进行正向遍历和逆向遍历.
//3.1 通过ListIterator进行正向遍历.
ListIterator<String> lit = list.listIterator();
while(lit.hasNext()) {
String s = lit.next();
System.out.println(s);
}
System.out.println("----------------------------");
while(lit.hasPrevious()) {
String s = lit.previous();
System.out.println(s);
}
System.out.println(2 << 3); //每往左移动一次, 相当于乘以 2
System.out.println((2 << 4) - 1 );
}
}
通过列表迭代器进行逆向遍历之前, 必须先进行一次正向遍历.
二、并发修改异常
三、Collection集合
1、概述
Collection集合是单例集合的顶层接口, 里边定义了所有单列集合都共有的内容.
2、创建Collection对象
因为Collection是接口, 所以不能直接通过new关键字来创建它的对象, 那它如何创建对象呢?
我们可以通过多态的方式, 创建其子类对象, 从而实现创建Collection接口对象. 代码如下:
Collection list = new ArrayList();
解释:
集合后边的<数据类型>是泛型的意思, 泛型是用来限定集合中存储元素的数据类型的.
例如:
string说明集合中只能存储字符串.
int说明集合中只能存储整数.
泛型只能是引用数据类型, 且在实际开发中, 泛型一般只结合集合来一起使用.
下面的可以先了解一下,混个眼熟.
Collection(集合):容器,用于存放对象(引用类型。基本类型需要自动装箱)
List(列表):元素有序,元素可以重复 (有索引)。 通过元素的equals()方法判断是否重复。
Set(集):元素无序,不可重复 (没有索引)。 遍历只能用Iterator迭代器和增强for, 不能使用普通for遍历。
ArrayList(数组列表): 查询快,增删慢。
LinkedList(链表): 查询慢,增删快。
HashSet(哈希表): 查询快,增删慢(底层其实就是Map) 。存放的引用类型需重写hashCode()和equals()方法。
LinkedHashSet(哈希链表): 查询慢,增删快。 有序的,存放顺序和取出顺序一致。
案例一(Collection练习)
1、需求
创建Collection集合对象, 用来存储字符串.
调用Collection接口的add()方法, 往上述集合中添加3个字符串, 内容如下:
“hello”, “world”, “java”
通过输出语句, 直接打印集合对象, 并观察结果.
2、参考代码
代码:
public class Demo01 {
public static void main(String[] args) {
//1.创建Collection集合对象, 用来存储字符串.
//Collection<String> coll = new ArrayList<String>();
//JDK1.7以后, 可以简化成: 如下格式
Collection<String> coll = new ArrayList<>();
//2.调用Collection接口的add()方法, 往上述集合中添加3个字符串, 内容如下:"hello", "world", "java"
coll.add("hello");
coll.add("world");
coll.add("java");
//3.通过输出语句, 直接打印集合对象, 并观察结果.
System.out.println("coll: " + coll); //ArrayList类已经重写了Object#toString()方法.
}
}
Collection集合的成员方法
常用方法如下:
public boolean add(E e) 添加元素.
public boolean remove(Object obj) 从集合中移除指定的元素.
public void clear() 清空集合对象
public boolean contains(Object obj) 判断集合中是否包含指定的元素
public boolean isEmpty() 判断集合是否为空
public int size() 获取集合的长度, 即集合中元素的个数
需求
通过多态的形式, 创建Collection集合对象.
调用Collection接口的add()方法, 往上述集合中添加3个字符串, 内容如下:
“hello”, “world”, “java”
分别测试上述的6个成员方法.
参考代码
public class Demo02 {
public static void main(String[] args) {
//1.通过多态的形式, 创建Collection集合对象.
Collection<String> coll = new ArrayList<>();
//2.调用Collection接口的add()方法, 往上述集合中添加3个字符串, 内容如下: hello", "world", "java"
//测试 public boolean add(E e) 添加元素, ArrayList类的add()方法返回值是true, 因为: List体系的特点是: 有序, 可重复.
coll.add("hello");
coll.add("world");
coll.add("java");
//3.分别测试上述的6个成员方法.
//测试 public boolean remove(Object obj) 从集合中移除指定的元素.
/* System.out.println(coll.remove("world")); //元素存在就移除, 返回true
System.out.println(coll.remove("world123")); //元素不存在就返回false.*/
//测试 public void clear() 清空集合对象
//coll.clear();
//测试 public boolean contains(Object obj) 判断集合中是否包含指定的元素
/*System.out.println(coll.contains("hello"));
System.out.println(coll.contains("java"));
System.out.println(coll.contains("java123"));*/
//测试 public boolean isEmpty() 判断集合是否为空 集合的长度为0, 返回true, 否则返回false
//System.out.println(coll.isEmpty());
//测试 public int size() 获取集合的长度, 即集合中元素的个数
System.out.println(coll.size());
//打印集合对象:
System.out.println("coll: " + coll);
}
}
案例二(Collection接口)
遍历集合
3.5.1、需求
通过多态的形式, 创建Collection集合对象.
调用Collection接口的add()方法, 往上述集合中添加3个字符串, 内容如下:
“hello”, “world”, “java”
通过迭代器遍历集合, 获取到每一个元素, 并打印到控制台上.
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
//案例: 演示集合的遍历.
/*
涉及到的方法:
Collection集合中的方法
public Iterator<E> iterator(); 根据集合对象, 获取其对应的迭代器对象.
Iterator迭代器中的方法:
public boolean hasNext(); 判断迭代器中是否有下一个元素.
public E next(); 获取迭代器中的下一个元素.
结论: 总结的集合的使用步骤 记忆
集合的使用分为4大步3小步.
1. 创建集合对象.
2. 创建元素对象.
3. 把元素对象添加到集合对象中.
4. 遍历集合.
4.1 根据集合对象获取其对应的迭代器对象. Collection#iterator();
4.2 判断迭代器中是否有下一个元素. Iterator#hasNext();
4.3 有就获取. Iterator#next();
*/
public class Demo03 {
public static void main(String[] args) {
//1.通过多态的形式, 创建Collection集合对象.
Collection<String> coll = new ArrayList<>();
//2.调用Collection接口的add()方法, 往上述集合中添加3个字符串, 内容如下:"hello", "world", "java"
/*String s1 = "hello";
coll.add(s1);*/
coll.add("hello");
coll.add("world");
coll.add("java");
//3.通过迭代器遍历集合, 获取到每一个元素, 并打印到控制台上.
//3.1 根据集合对象获取其对应的迭代器对象.
//多态
//接口 Iterator接口的子类对象
Iterator<String> it = coll.iterator();
//3.2 判断迭代器中是否有下一个元素.
while (it.hasNext()) {
//3.3 有就获取, 然后输出.
String s = it.next();
System.out.println(s);
}
}
}
总结: 集合的使用步骤
集合的步骤可以分为四大步三小步, 具体如下:
创建集合对象.
创建元素对象.
把元素添加到集合中.
遍历集合
根据集合对象获取其对应的迭代器对象.
通过Collection#iterator()方法实现.
判断迭代器中是否有下一个元素.
通过Iterator#hasNext()方法实现.
如果有, 就获取该元素.
通过Iterator#next()方法实现.
案例三(collection遍历)
存储自定义对象并遍历
1、需求
定义一个学生类, 属性为姓名和年龄.
创建Collection集合, 用来存储学生对象.
往Collection集合中, 添加3个学生的信息.
通过迭代器, 遍历集合.
2、参考代码
//案例: 演示Collection集合存储自定义对象, 并遍历
public class Demo04 {
public static void main(String[] args) {
//1. 创建集合对象.
Collection<Student> coll = new ArrayList<>();
//2. 创建元素对象.
Student s1 = new Student("刘亦菲", 33);
Student s2 = new Student("赵丽颖", 31);
Student s3 = new Student("高圆圆", 35);
//Student s4 = new Student("丹丹", 18);
//3. 把元素对象添加到集合对象中.
coll.add(s1);
coll.add(s2);
coll.add(s3);
//coll.add(s4);
//4. 遍历集合.
//4.1 根据集合对象获取其对应的迭代器对象. Collection#iterator();
Iterator<Student> it = coll.iterator();
//4.2 判断迭代器中是否有下一个元素. Iterator#hasNext();
while (it.hasNext()) {
//4.3 有就获取. Iterator#next();
Student stu = it.next();
System.out.println(stu);
//不能写成如下的形式, 因为next()方法调用一次, 就会获取一个值.
//下边这个代码就属于: 判断一次, 获取两个值.
//System.out.println(it.next().getName() + "..." + it.next().getAge());
}
}
}
四、List集合
熟记:list有序集合,可以重复,有索引(从0开始)
set无序集合,不可重复
4.1 案例一(迭代器)
1、需求
创建List集合, 用来存储字符串.
往List集合中添加4个字符串值, 分别是: “hello”, “world”, “java”, “world”
通过迭代器, 遍历List集合, 获取每一个元素, 并打印到控制台上.
2、参考代码
代码:(list是子接口, List list = new ArrayList<>();)
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
//案例: 演示List集合入门.
/*
List集合简介:
概述:
它是Collection集合的子接口, 表示序列.
特点: //记忆
1. 元素有序. //指的是元素的存取顺序一致, 注意: 不是排序.
. //指的是集合中可以存储重复的数据.
3. 元素有索引. //索引从0开始.
*/
public class Demo01 {
public static void main(String[] args) {
//1.创建List集合, 用来存储字符串.
List<String> list = new ArrayList<>();
//2.往List集合中添加4个字符串值, 分别是: "hello", "world", "java", "world"
list.add("hello");
list.add("world");
list.add("java");
list.add("world");
//3.通过迭代器, 遍历List集合, 获取每一个元素, 并打印到控制台上.
//3.1 获取迭代器.
Iterator<String> it = list.iterator();
//3.2 判断有没有元素.
/*while (it.hasNext()) {
//3.3. 获取元素.
String s = it.next();
System.out.println(s);
}*/
//简化版
while (it.hasNext())
//3.3. 获取元素.
System.out.println(it.next());
}
}
4.2、List集合特有的成员方法
1、常用方法
public void add(int index, E element)
解释: 在集合的指定位置(索引), 插入指定的元素, 索引越界会报IndexOutOfBoundsException异常.
public E remove(int index)
解释: 删除指定索引处的元素, 并返回被删除的元素, 索引越界会报IndexOutOfBoundsException异常.
public E set(int index, E element)
解释: 修改指定索引处的元素为指定的值, 并返回修改前的元素, 索引越界会报IndexOutOfBoundsException异常.
public E get(int index)
解释: 根据索引, 获取其对应的元素, 索引越界会报IndexOutOfBoundsException异常.
2、需求
创建List集合, 用来存储字符串.
往List集合中添加3个字符串值, 分别是: “hello”, “world”, “java”.
演示上述的4个方法.
3、代码演示
import java.util.ArrayList;
import java.util.List;
/*
List集合中的特有成员方法:
public void add(int index, E element); 在指定的位置插入指定的元素
public E remove(int index); 根据索引, 移除指定的元素, 并返回此元素.
public E set(int index, E element) 修改指定索引处的元素未指定的值, 并返回修改前的元素.
public E get(int index) 根据索引, 获取其对应的元素.
细节:
上述的四个方法, 涉及到的索引如果不存在, 则会报: IndexOutOfBoundsException(索引越界异常)
*/
public class Demo02 {
public static void main(String[] args) {
//1.创建List集合, 用来存储字符串.
List<String> list = new ArrayList<>();
//2.往List集合中添加3个字符串值, 分别是: "hello", "world", "java".
list.add("hello");
list.add("world");
list.add("java");
//3.演示上述的4个方法.
//测试 public void add(int index, E element); 在指定的位置插入指定的元素
//list.add(10, "黑马程序员"); //报错, 索引越界.
//list.add(3, "黑马程序员"); //不报错
//测试 public E remove(int index); 根据索引, 移除指定的元素, 并返回此元素.
//System.out.println(list.remove(11)); //报错, 索引越界.
//System.out.println(list.remove(1)); //不报错
//测试public E set(int index, E element) 修改指定索引处的元素未指定的值, 并返回修改前的元素.
//list.set(10, "黑马程序员"); //报错, 索引越界.
//list.set(3, "黑马程序员"); //报错, 索引越界.
//list.set(1, "黑马程序员"); //不报错
//测试 public E get(int index) 根据索引, 获取其对应的元素.
/*System.out.println(list.get(0));
System.out.println(list.get(1));
System.out.println(list.get(2));
System.out.println(list.get(3));*/
//打印集合.
System.out.println("list: " + list);
System.out.println("------------------------");
List<Integer> list2 = new ArrayList<>();
list2.add(1);
list2.add(2);
list2.add(3);
list2.add(4);
list2.add(5);
list2.remove(3); //如果集合的泛型是Integer类型, 传入值后, 是按照索引走的.
System.out.println(list2); //1235
}
}
4.2.1案例二(List遍历)
遍历List集合
1、需求
创建List集合, 用来存储字符串.
往List集合中添加3个字符串值, 分别是: “hello”, “world”, “java”.
通过**for循环 + size() + get()**的形式, 遍历List集合.
4.4.2、参考代码
代码:(for循环遍历List集合的快捷键: itli)
import java.util.ArrayList;
import java.util.List;
//案例: 演示List集合(独有)的遍历方式.
//思路: 通过for循环 + size() + get()的形式, 遍历List集合.
public class Demo03 {
public static void main(String[] args) {
//1.创建List集合, 用来存储字符串.
List<String> list = new ArrayList<>();
//2.往List集合中添加3个字符串值, 分别是: "hello", "world", "java".
list.add("hello");
list.add("world");
list.add("java");
//3. 通过for循环 + size() + get()的形式, 遍历List集合.
//分解版
/*System.out.println(list.get(0));
System.out.println(list.get(1));
System.out.println(list.get(2));*/
//for循环版本
for (int i = 0; i < list.size(); i++) {
String s = list.get(i);
System.out.println(s);
}
//补充: for循环遍历List集合的快捷键: itli
}
}
4.2.2 案例三(List遍历)
定义一个学生类, 属性为姓名和年龄
创建List集合, 用来存储学生对象
往List集合中, 添加3个学生的信息
分别通过两种遍历方式, 来遍历List集合
public class Demo04 {
public static void main(String[] args) {
//1.定义一个学生类, 属性为姓名和年龄.
//2.创建List集合, 用来存储学生对象.
List<Student> list= new ArrayList<>();
//3.往List集合中, 添加3个学生的信息.
Student s1 = new Student("刘亦菲", 33);
Student s2 = new Student("赵丽颖", 31);
Student s3 = new Student("高圆圆", 35);
list.add(s1);
list.add(s2);
list.add(s3);
//4.分别通过两种遍历方式, 来遍历List集合.
//方式一: 通过迭代器实现.
Iterator<Student> it = list.iterator();
while (it.hasNext()) {
/*Student stu = it.next();
System.out.println(stu);*/
System.out.println(it.next());
}
System.out.println("--------------------");
//方式二: 通过for() + size() + get()方法实现.
for (int i = 0; i < list.size(); i++) {
Student stu = list.get(i);
System.out.println(stu);
}
}
}
五、并发修改异常
并发修改异常简介:
问题描述:
当使用普通迭代器(Iterator)遍历集合的同时, 又往集合中添加了元素, 就会报并发修改异常.
产生原因:
迭代器是依赖于集合而存在, 当判断成功后, 集合中添加了新的元素, 而迭代器不知道, 所以就报错了.
其实这个问题说的是: 迭代器遍历集合中的元素时, 不要使用集合对象去修改集合中的元素.
解决方案:
1通过 列表迭代器(ListIterator)来解决.
细节一: 必须使用列表迭代器中的添加元素的方法. ListIterator#add();
细节二: 这种方式添加的元素是在刚才迭代到的元素后边的.
2通过for循环 + size()方法来解决.
细节一: 这种方式添加的元素是在集合的最后位置添加的.
3通过CopyOnWriteArrayList集合解决
需求
创建List集合, 用来存储字符串.
往List集合中添加3个字符串值, 分别是: “hello”, “world”, “java”.
判断集合中是否有"world"元素, 如果有, 就往集合中添加一个"JavaEE".
public class Demo02 {
public static void main(String[] args) {
//1.创建List集合, 用来存储字符串.
List<String> list = new ArrayList<>();
//2.往List集合中添加3个字符串值, 分别是: "hello", "world", "java".
list.add("hello");
list.add("world");
list.add("java");
//3.判断集合中是否有"world"元素, 如果有, 就往集合中添加一个"JavaEE".
//使用普通的迭代器实现, 会报并发修改异常.
Iterator<String> it = list.iterator();
while(it.hasNext()) {
String s = it.next();
//判断是否是"world", 如果是, 就添加"JavaEE"
if ("world".equals(s)) {
list.add("JavaEE"); //报错
}
}
}
}
/*
并发修改异常简介:
问题描述:
当使用普通迭代器(Iterator)遍历集合的同时, 又往集合中添加了元素, 就会报并发修改异常.
产生原因:
迭代器是依赖于集合而存在, 当判断成功后, 集合中添加了新的元素, 而迭代器不知道, 所以就报错了.
其实这个问题说的是: 迭代器遍历集合中的元素时, 不要使用集合对象去修改集合中的元素.
解决方案:
1. 通过 列表迭代器(ListIterator)来解决.
细节一: 必须使用列表迭代器中的添加元素的方法. ListIterator#add();
细节二: 这种方式添加的元素是在刚才迭代到的元素后边的.
2. 通过for循环 + size()方法来解决.
细节一: 这种方式添加的元素是在集合的最后位置添加的.
3. 通过CopyOnWriteArrayList集合解决.
*/
public class Demo02 {
public static void main(String[] args) {
//1.创建List集合, 用来存储字符串.
List<String> list = new ArrayList<>();
//2.往List集合中添加3个字符串值, 分别是: "hello", "world", "java".
list.add("hello");
list.add("world");
list.add("java");
//3.判断集合中是否有"world"元素, 如果有, 就往集合中添加一个"JavaEE".
//方式一: 使用普通的迭代器实现, 会报并发修改异常.
/*Iterator<String> it = list.iterator();
while(it.hasNext()) {
String s = it.next();
//判断是否是"world", 如果是, 就添加"JavaEE"
if ("world".equals(s)) {
list.add("JavaEE"); //并发
}
}*/
//方式二: 使用列表迭代器实现.
/* ListIterator<String> lit = list.listIterator();
while(lit.hasNext()) {
String s = lit.next();
//判断是否是"world", 如果是, 就添加"JavaEE"
if ("world".equals(s)) {
//细节一: 必须使用列表迭代器中的添加元素的方法. ListIterator#add();
lit.add("JavaEE"); //并发
}
}*/
//方式三: 使用for + size()解决.
for (int i = 0; i < list.size(); i++) {
String s = list.get(i);
//判断是否是"world", 如果是, 就添加"JavaEE"
if ("world".equals(s)) {
list.add("JavaEE"); //并发
}
}
//4. 打印集合
System.out.println("list: " + list);
}
}
六、增强for循环
增强for的底层其实是通过迭代器(Iterator)实现的
增强for
1、概述
增强for是JDK1.5的新特性, 它是用来简化数组和Collection集合的遍历的.
2、格式
for(元素的数据类型 变量名 : 数组或者Collection集合对象) {
//上述的 变量名 代表的就是, 数组或者Collection集合中的每一个元素.
}
3、好处
增强for是用来简化数组和Collection集合的遍历的.
4、注意事项
要通过增强for遍历的数组或者Collection集合, 不能为null.
即: 增强for的目标要判断是否为null.
5、示例一: 遍历数组
5.1、需求
定义int类型的数组, 存储元素1, 2, 3, 4, 5.
通过增强for, 遍历上述的数组.
5.2、参考代码
代码:
/*
增强for:
概述:
JDK1.5的特性, 是用来简化遍历Collection或者数组操作的
格式:
for(数据类型 元素名 : 要遍历的集合或者数组) {
//元素名: 表示的就是集合/数组中的每一个元素
}
注意事项:
通过增强for来遍历的数组或者集合不能为null
的底层是一个: 迭代器
*/
public class Demo01 {
public static void main(String[] args) {
//1.定义int类型的数组, 存储元素1, 2, 3, 4, 5.
int[] arr = {1, 2, 3, 4, 5};
//arr = null;
//2.通过增强for, 遍历上述的数组.
for(int a: arr) {
//a: 表示的就是数组中的每一个元素
System.out.println(a);
}
//快捷键: iter
for (int i : arr) {
System.out.println(i);
}
}
}
案例三: 遍历集合
7.1、需求
定义学生类, 属性为姓名和年龄.
创建List集合, 用来存储学生对象.
往集合中添加3个学生的信息.
通过增强for遍历上述的List集合.
7.2、参考代码
代码:
public class Demo03 {
public static void main(String[] args) {
//1.定义学生类, 属性为姓名和年龄.
//2.创建List集合, 用来存储学生对象.
List<Student> list = new ArrayList<>();
//3.往集合中添加3个学生的信息.
/*Student s1 = new Student("刘亦菲", 33);
list.add(s1);*/
list.add(new Student("刘亦菲", 33));
list.add(new Student("赵丽颖", 31));
list.add(new Student("高圆圆", 35));
//4.通过增强for遍历上述的List集合.
for (Student s : list) {
System.out.println(s);
}
}
}
七、ArrayList集合
LinkedList的特有方法
LinkedList集合主要是用来操作头和尾元素的, 所以它里边定义了大量这类的方法, 常用的方法如下:
public void addFirst(E e) 往列表的开头插入指定的元素
public void addLast(E e) 往列表的末尾插入指定的元素
public E removeFirst() 删除列表中的第一个元素, 并返回被删除的元素
public E removeLast() 删除列表中的最后一个元素, 并返回被删除的元素.
public E getFirst() 返回列表的第一个元素
public E getLast() 返回列表的最后一个元素
八、LinkedList集合
3.1、案例一: LinkedList入门
需求
创建LinkedList集合对象, 存储字符串数据: “hello”, “world”, “java”
遍历LinkedList集合.
参考代码
import java.util.Iterator;
import java.util.LinkedList;
import java.util.ListIterator;
//案例: 演示LinkedList集合入门.
public class Demo01 {
public static void main(String[] args) {
//1.创建LinkedList集合对象, 存储字符串数据: "hello", "world", "java"
LinkedList<String> list = new LinkedList<>();
list.add("hello");
list.add("world");
list.add("java");
//2.遍历LinkedList集合.
//方式一: 普通的迭代器.
Iterator<String> it = list.iterator();
while (it.hasNext()) {
String s = it.next();
System.out.println(s);
}
System.out.println("--------------------------------");
//方式二: 普通for
for (int i = 0; i < list.size(); i++) {
String s = list.get(i);
System.out.println(s);
}
System.out.println("--------------------------------");
//方式三: 增强for
for (String s : list) {
System.out.println(s);
}
System.out.println("--------------------------------");
//方式四: 列表迭代器
ListIterator<String> lit = list.listIterator();
while (lit.hasNext()) {
String s = lit.next();
System.out.println(s);
}
System.out.println("--------------------------------");
//方式五: 转数组遍历
Object[] objs = list.toArray();
for (Object obj : objs) {
System.out.println(obj);
}
}
}
8.1 LinkedList的特有方法
LinkedList集合主要是用来操作头和尾元素的, 所以它里边定义了大量这类的方法, 常用的方法如下:
public void addFirst(E e) 往列表的开头插入指定的元素
public void addLast(E e) 往列表的末尾插入指定的元素
public E removeFirst() 删除列表中的第一个元素, 并返回被删除的元素
public E removeLast() 删除列表中的最后一个元素, 并返回被删除的元素.
public E getFirst() 返回列表的第一个元素
public E getLast() 返回列表的最后一个元素
九 Set类
9.1 Set集合
1.1 概述
Set集合是Collection集合的子体系, 它的元素特点是无序, 唯一,无下标.
注意;
1.Set集合是一个接口, 所以不能通过new的方式直接创建它的对象.
2.Set集合中没有带索引的方法, 所以不能通过普通for循环遍历.
3.Set集合的常用子类主要有两个, 分别是HashSet集合和TreeSet集合
Set入门
1.2.1 需求
创建Set集合对象, 存储字符串数据: “hello”, “world”, “java”, “world”
通过两种方式, 遍历Set集合.
public static void main(String[] args) {
Set<String> set = new HashSet<>();
set.add("Hello");
set.add("World");
set.add("java");
System.out.println("Set没有索引,所以没办法用for循环,利用迭代器");
for (String s : set) {
System.out.println("s = " + s);
}
System.out.println("+++++++++++++++++++++++++++++++");
Iterator<String> iterator = set.iterator();
while (iterator.hasNext()){
System.out.println(iterator.next());
}
}
9.2 哈希值
所谓的哈希值是JDK根据对象的地址,或者字符串,或者数字算出来的int类型的数值.
如何获取哈希值
可以通过Object#hashCode()方法, 获取指定对象的哈希值, 具体如下:
public int hashCode(); //根据对象, 获取其对应的哈希值.
案例: 哈希值入门
需求
定义学生类, 属性为姓名和年龄.
在测试类的main方法中, 创建两个学生对象, 分别获取它们的哈希值, 并打印.
测试: 重写Object#hashCode()方法, 实现不同对象的哈希值也是相同的.
测试: 同一对象哈希值肯定相同, 不同对象哈希值一般不同.
public static void main(String[] args) {
Student s1 = new Student("张三",19);
Student s2 = new Student("李四",20);
Student s3 = new Student("王五",21);
Student s4 = new Student("王五",21);
int i = s1.hashCode();
int i1 = s2.hashCode();
int i2 = s3.hashCode();
int i3= s3.hashCode();
System.out.println(i);
System.out.println(i1);
System.out.println(i2);
System.out.println(i3);
}
console:
460141958
1163157884
1956725890
1956725890
结论:同一对象多次调用hashCode()方法,返回的哈希值相同.
同一个对象的哈希值肯定相同,不同对象的哈希值一般不同.
9.3 HashSet集合
特点:
底层数据结构是HashSet表.
对集合的迭代顺序不做任何保证,也就是说不保证元素的存取顺序一致.
没有带索引的方法,不能通过for循环遍历.
由于是Set集合, 所以是不包含重复元素的集合.
总结:
HashSet集合的特点是: 无序, 唯一, 元素无索引, 它的底层数据结构是: 哈希表.
案例一: 存储字符串并遍历
需求
定义HashSet集合, 存储字符串"hello", “world”, “java”, “world”
遍历HashSet集合, 打印每一个元素值, 并观察程序的运行结果.
HashSet<String> hs = new HashSet<>();
hs.add("hello");
hs.add("world");
hs.add("java");
hs.add("world");
Iterator<String> iterator = hs.iterator();
while (iterator.hasNext()){
System.out.println("iterator = " + iterator.next());
}
System.out.println("-----------------");
for (String h : hs) {
System.out.println(h);
}
console:
iterator = world
iterator = java
iterator = hello
-----------------
world
java
hello
9.3.1 源码剖析
那么Hash Set是如何保证唯一的呢,通过源码可以发现是这一行代码里面经过一步步判断得出的结论
[|| a或者b (有true则true ) && a和b (有false则false)]
if (p.hash == hash && ((k = p.key) == key || ( key != null && key.equals(k)))){
一开始我们直接判断内容是肯定也是可以的,只不过哈希表的速度相对于equals一个一个拼接的对比会快很多,所以利用哈希表做判断
具体的判断流程:
1.判断两个对象(集合中已经存在的某个元素 和 要添加的元素)的哈希值是否相同.
哈希值相同: 说明可能是同一个元素, 程序继续往下执行.
哈希值不同: 说明不是同一个元素, 就添加
2.比较两个对象的地址值是否相同
地址值相同: 说明是同一个元素, 不添加.
地址值不同: 说明可能不是同一个对象, 程序继续往下执行.
3.判断要添加的元素是否为null
如果为null: 不是同一个元素, 就添加.
如果不为null: 说明可能是同一个元素, 程序继续往下执行.
4.比较对象的各个属性值是否相同
如果相同: 说明是同一个元素, 不添加.
如果不同: 说明不是同一个元素, 就添加.
问题: 为什么要设计的这么复杂呢? 直接通过equals()比较两个属性的属性值不就好了吗?
答案: 确实可以这样设计, 但是效率比较低, 程序之所以写的这么繁琐, 就是为了降低调用equals()方法的次数, 从而实现 节约资源, 提高效率!
结论
HashSet保证元素的唯一性, 依赖hashCode()和equals()方法.
9.4常见数据结构之哈希表
在存储的时候, 会将上述的哈希值和16进行取余操作, 然后根据余数值进行存储.
⦁ JDK8以前, 底层采用数组 + 链表的形式实现, 可以理解为: 一个元素为链表的数组.
⦁ JDK8以后, 在长度比较长的时候, 底层实现了 利用红黑树的优化.
案例二
创建HashSet集合, 用来存储学生对象, 并往其中添加3个学生的信息.
遍历集合, 并把结果打印到控制台上.
public static void main(String[] args) {
//⦁ 创建HashSet集合, 用来存储学生对象, 并往其中添加3个学生的信息.
HashSet<Student> hs = new HashSet<>();
hs.add(new Student("刘亦菲", 33));
hs.add(new Student("赵丽颖", 31));
hs.add(new Student("高圆圆", 35)); //---------------------①
hs.add(new Student("高圆圆", 35)); //---------------------②
//⦁ 遍历集合, 并把结果打印到控制台上.
Iterator<Student> iterator = hs.iterator();
while (iterator.hasNext()){
System.out.println("iterator = " + iterator.next().toString());
}
System.out.println("-----------------");
for (Student h : hs) {
System.out.println(h);
}
console:
iterator = Student{name='刘亦菲', age=33}
iterator = Student{name='赵丽颖', age=31}
iterator = Student{name='高圆圆', age=35}
-----------------
Student{name='刘亦菲', age=33}
Student{name='赵丽颖', age=31}
Student{name='高圆圆', age=35}
在①②的地方如果插入的数据是一样的,则需要在Student类中重写equals和hashCode
细节: 在Student类中重写hashCode()和equals()方法, 即可保证元素的唯一性.
9.5 LinkedHashSet集合
LinkedHashSet集合是HashSet的集合
特点:
1.底层的数据结构是通锅哈希表+链表实现的,具有可预测的迭代次序.
2.由链表保证元素有序,也就是说与那素的存取顺序是一致的,
3.由哈希表保证元素唯一,也就是说没有重复的元素.
总结:
LinkedHashSet集合的特点是:有序,唯一.
案例: LinkedHashSet入门
创建LinkedHashSet集合对象, 存储字符串"hello", "world", "java", "world"
遍历集合, 并将结果打印到控制台上.
public static void main(String[] args) {
LinkedHashSet<String> lhs = new LinkedHashSet<>();
lhs.add("hello");
lhs.add("world");
lhs.add("java");
lhs.add("world");
lhs.add("2");
lhs.add("2");
lhs.add("world");
for (String lh : lhs) {
System.out.println(lh);
}
System.out.println("-----------------");
Iterator<String> iterator = lhs.iterator();
while (iterator.hasNext()){
System.out.println(iterator.next());
}
console:
hello
world
java
2
-----------------
hello
world
java
2
linkedhashset集合特点是:唯一有序
9.6 可变参数
如果遇到让我们定义一些方法,分别获取2个整数,3个整数,4个整数的和,此时我们只能通过方法重载的形式来实现,但是比较麻烦,因为只有参数列表的变法,所以我们可以通过可变参数来优化
格式:
修饰符 返回值类型 方法名(数据类型… 变量名){ }
注意事项
1.这里的变量是一个数组
2.如果一个方法有多个参数,其中包含可变参数,可变参数要放在最后.
(方法的形参列表有且只能有一个可变参数)
求整数和
定义getSum()方法, 用来获取n个整数的和(n可能是任意的一个数字).
在main方法中, 调用getSum()方法.
public static void main(String[] args) {
int[] arr ={13,5,5,65,7};
int sum = getSum(arr);
System.out.println("sum = " +sum );
}
private static int getSum(int... arr) {
int sum = 0;
for (int i = 0; i < arr.length; i++) {
sum+=arr[i];
}
return sum;
}
console:
95
例题
第一题: 通过代码, 完成如下需求:
-
自定义一个学生类,给出成员变量name和age.
-
使用Collection集合存储学生对象并遍历.
-
最后在控制台输出学生对象的成员变量值。
//格式为: 姓名: 张三, 年龄: 23 -
通过两种方式遍历.
//提示: 普通迭代器, 增强for.
package CollectionDemo;
import javax.swing.text.html.HTMLDocument;
import java.awt.*;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.ListIterator;
public class CollectionDemo {
public static void main(String[] args) {
Collection<Student> coll = new ArrayList<Student>();
coll.add(new Student("张三",18));
coll.add(new Student("李四",19));
coll.add(new Student("王五",20));
System.out.println(coll);
System.out.println("--------------------");
for (Student student : coll) {
System.out.println(student);
}
System.out.println("--------------------");
Iterator<Student> it = coll.iterator();
while (it.hasNext()){
Student s= it.next();
System.out.println(s);
}
}
}
第二题: 通过代码, 完成如下需求:
-
自定义一个学生类,给出成员变量name和age.
-
使用List集合存储学生对象并遍历.
-
最后在控制台输出学生对象的成员变量值。
//格式为: 姓名: 张三, 年龄: 23 -
通过两种方式遍历.
//提示: 普通迭代器, 增强for.
package CollectionDemo;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
public class CollectionDemo_02 {
public static void main(String[] args) {
//collection有两大子体系
//list 有序,可重复
//Set 无序,唯一
List<Student> list = new ArrayList<Student>();
list.add(new Student("张三",18));
list.add(new Student("李四",19));
list.add(new Student("王五",20));
System.out.println(list);
System.out.println("--------------------");
for (Student student : list) {
System.out.println(student);
}
System.out.println("--------------------");
Iterator<Student> it = list.iterator();
while (it.hasNext()){
Student s= it.next();
System.out.println(s);
}
}
}
第三题: 通过代码, 完成如下需求:
- 自定义一个人类,给出成员变量name和age.
- 使用ArrayList集合存储人类对象并遍历.
- 最后在控制台输出人类对象的成员变量值。
//格式为: 姓名: 张三, 年龄: 23 - 通过四种方式遍历.
//提示: 普通迭代器, 增强for, 列表迭代器, 普通for
package CollectionDemo;
import com.sun.org.apache.bcel.internal.generic.ARRAYLENGTH;
import com.sun.org.apache.xml.internal.utils.ListingErrorHandler;
import java.awt.event.MouseWheelListener;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.ListIterator;
public class CollectionDemo3 {
public static void main(String[] args) {
ArrayList<Student> arrayList = new ArrayList<Student>();
arrayList.add(new Student("张三",19));
arrayList.add(new Student("李四",22));
arrayList.add(new Student("王五",33));
arrayList.add(new Student("赵柳",44));
System.out.println("-------普通迭代器--------------");
Iterator<Student> iterator = arrayList.iterator();
while (iterator.hasNext()){
Student s= iterator.next();
System.out.println(s);
}
System.out.println("--------增强for-------------");
for(Student student:arrayList){
System.out.println(student);
}
System.out.println("----------列表迭代器-----------");
ListIterator<Student> listIterator = arrayList.listIterator();
while (listIterator.hasNext()){
Student student = listIterator.next();
System.out.println(student);
}//如果通过列表迭代器进行逆向遍历,则需要先正向遍历一遍之后再逆向遍历一次不然会报并发修改异常
System.out.println("---------普通for------------");
for (int i = 0; i < arrayList.size(); i++) {
Student student = arrayList.get(i);
System.out.println(student);
}
}
}
第四题: 通过代码, 完成如下需求:
- 自定义一个学生类,给出成员变量name和age.
- 定义ArrayList集合, 用来存储学生对象.
- 键盘录入3个学生的信息, 将他们封装成学生对象后, 添加到ArrayList集合中.
- 判断集合中是否有姓名叫 刘亦菲 的学生, 如果有, 就往集合中添加学生对象: 糖糖, 18
- 遍历集合. //用任意一种方式遍历即可.
public static void main(String[] args) {
ArrayList<Student> arrayList = new ArrayList<Student>();
arrayList.add(new Student("张三", 18));
arrayList.add(new Student("李四", 19));
arrayList.add(new Student("王五", 20));
if ("刘亦菲".equals(arrayList.size())) {
} else {
arrayList.add(new Student("糖糖", 18));
}
System.out.println(arrayList);
System.out.println("--------------------");
for (Student student : arrayList) {
System.out.println(student);
}
}
第五题: 通过代码, 完成如下需求:
- 定义LinkedList集合, 存储字符串数据, 例如: “hello”, “world”, “java”.
kedList集合中常用的方法.
addFirst(), addLast(), removeFirst(), removeLast(), getFirst(), getLast()
public static void main(String[] args) {
LinkedList<String> linkedList = new LinkedList<String>();
linkedList.add("hello");
linkedList.add("world");
linkedList.add("java");
System.out.println("addfirst");
linkedList.addFirst("我第一个");
System.out.println("addLast()");
linkedList.addLast("我最后一个");
System.out.println(linkedList);
System.out.println("------------");
System.out.println("removeFirst()");
linkedList.removeFirst();
System.out.println("removeFirst()");
linkedList.removeLast();
System.out.println(linkedList);
System.out.println("------------");
linkedList.getFirst();
linkedList.getLast();
System.out.println("------------");
}
第六题: 通过代码, 完成如下需求:
- 定义ArrayList集合,存入多个字符串, 例如: “ab1”, “123ad”, “def”, “bca”, “def”, “def”, “dadfadf” “dddaaa” “你好啊” “我来啦” “别跑啊”
- 遍历集合, 删除长度大于5的字符串, 打印删除后的集合对象
- 基于上一步, 删除集合中元素包含0-9数字的字符串(只要字符串中包含0-9中的任意一个数字就需要删除此整个字符串), 打印删除后的集合对象.
- 基于上一步, 删除集合中所有的"def"字符串, 打印删除后的集合对象.
package CollectionDemo;
import java.util.ArrayList;
import java.util.Iterator;
public class CollectionDemo_06 {
public static void main(String[] args) {
ArrayList<String> arrayList = new ArrayList<>();
arrayList.add("ab1");
arrayList.add("123ad");
arrayList.add("def");
arrayList.add("bca");
arrayList.add("def");
arrayList.add("def");
arrayList.add("dadfadf");
arrayList.add("dddaaa");
arrayList.add("你好啊");
arrayList.add("我来啦");
arrayList.add("别跑啊");
System.out.println(arrayList);
Iterator<String> iterator =arrayList.iterator();
while (iterator.hasNext()){
String s = iterator.next();
if( s.length()> 5 || s.contains("def") || !contains(s) ){
iterator.remove();
}
}
System.out.println(arrayList);
}
public static boolean contains(String str) {
for (int i = 0; i < str.length(); i++) {
char ch = str.charAt(i);
if(ch >= '0' && ch <= '9');
return true;
}
return false;
}
}