今日任务

1、List接口介绍(掌握常用List特有方法)

2、练习

3、ArrayList介绍(必须清楚集合的特征、掌握集合中的方法)

4、LinkedList介绍(必须清楚集合的特征、掌握集合中的方法)

5、Vector 类介绍(了解)

6、List下的子类总结(掌握)

7、Set 接口介绍(掌握Set集合的特性)

8、HashSet 集合(掌握HashSet集合的应用)

1、List接口介绍

在学习Collection接口的时候,api中告诉我们,在Collection接口的下面有2个直接的子接口,分别是Set和List。我们这里先学习List接口。

List接口:

1)是Collection接口的子接口,继承了Collection接口中的所有方法;

2)List接口定义的所有集合中的元素都可以重复,并且还可以保证存取的顺序一致(存储和取出的顺序一致的);

3)List接口下的所有集合全部拥有下标,List接口更像数组;

4)由于List接口规定自己的真实实现类(集合)都拥有下标,因此我们在操作List接口下的所有集合容器的时候,都可以通过下标操作;

5)因此在List接口中它不仅仅继承到Collection接口中的所有函数,同时java 还根据List的下标的特性,定义了适合List接口的特有函数;

1.1、List的特有方法

List集合和Collection集合不同之处,就是List接口中的特有方法都是围绕集合的下标而设计的。

List集合中的特有方法:

1)添加方法:

void add(int index,Object element ):根据指定的角标位置,向集合中添加指定的元素对象;

index >= 0 && index <= size();

boolean addAll(int index, Collection coll):根据指定的角标位置,向集合中添加指定集合中所有的元素;

index >= 0 && index <= size();

分析和步骤:

1)定义一个ListDemo类,在这个类中定义一个method_1()函数;

2)在method_1()函数中使用new关键字创建ArrayList类的对象list,并赋值给接口List类型;

3)使用集合对象list调用属于Collection接口中的add(E e)函数向集合List中添加字符串”aaaa”;

4)使用集合对象list调用属于接口List中特有的函数add(int index,Object element )根据指定的下标向集合中添加数据”abc”;

5)使用输出语句输出list对象中的值;

public static void method_1() { // 创建集合对象 List list = new ArrayList(); List list1 = new ArrayList(); // 向集合中添加数据 list.add("aaaa"); list.add("bbbb"); list.add("cccc"); list1.add("hhhh"); list1.add("哈哈"); // 使用List接口中特有的函数向接口中添加数据 // list.add(1, "dddd");//1表示要添加数据的下标位置 // list.add(4,"xyz");//指定的下标前面一定要有元素 // 向集合list的下标为2的位置添加集合list1中所有的数据 list.addAll(2, list1); // 输出数据 System.out.println(list); }

注意:

1)指定的下标前面一定要有数据;

2)添加不是覆盖,指定的位置添加了元素后,之前存在元素就会向后移动;

2)获取方法:

List subList(int startIndex, int endIndex): 返回集合中从指定角标开始(包含头角标)到指定角标结束(不包含尾角标)的所有元素,以集合方式返回

int indexOf(Object obj)返回指定元素在集合中第一次出现的角标。没有匹配元素则返回-1

分析和步骤:

1)在上述ListDemo类中定义一个method_2()函数;

2)在method_2()函数中使用new关键字创建ArrayList类的对象list,并赋值给接口List类型;

3)使用集合对象list调用属于Collection接口中的add(E e)函数向集合List中添加几个字符串数据;

4) 使用集合对象list调用属于接口List中特有的函数get(int index )根据指定的下标获取下标对应的数据;

5)使用输出语句输出获得的数据obj;

public static void method_2() { // 创建集合对象 List list = new ArrayList(); // 向集合中添加数据 list.add("nba"); list.add("nba"); list.add("cba"); list.add("wbna"); list.add("wcba"); // 根据下标获得对应的元素 // Object obj = list.get(2); // Object obj = list.get(7);//下标不能是空白区域 // System.out.println(obj); // List subList = list.subList(1,0);//结束角标要大于等于起始角标 // System.out.println(subList); // 返回指定元素第一次出现的角标 int index = list.indexOf("nbaa");// 没有返回-1 System.out.println(index); }

注意: 下标的范围:index >= 0 && index <= size()-1;

1.2、List的特有迭代器

List接口继承了Collection接口,Collection接口中的获取某个集合对应的迭代器的函数,List一定也继承到了。但是由于List集合有下标,因此Java中针对List这类集合同时也提供了自己特有的迭代器。

ListIterator接口:

ListIterator接口:它是专门针对List接口而设计的,这个迭代器在遍历List集合的时候,可以对这个集合中的元素进行增 删 改 查操作。并且它还可以逆向遍历。

注意:ListIterator迭代器只能被List集合使用,其它集合都不能使用。

使用ListIterator逆向迭代的时候一定要使用带有参数的listIterator(index)函数,然后集合的长度作为参数下标,否则该迭代光标就会从前往后迭代;

这里的index>=0&&index<=list.size();

使用list.size()作为参数。

分析和步骤:

1)定义一个ListIteratorDemo类;

2)在这个类中使用new关键字创建ArrayList类的对象list,并赋值给接口List类型;

3)使用集合对象list调用属于Collection接口中的add(E e)函数向集合List中添加几个字符串数据;

4)使用集合对象list调用属于接口Collection中的iterator()函数获得迭代器对象it,并在for循环中使用迭代器对象it调用hasNext()函数判断是否有元素,使用迭代器对象it调用next()函数获取list集合中的数据,并打印;

5)使用集合对象list调用listIterator()函数生成ListIterator 类型,并使用for循环对其迭代遍历;

6)同样使用ListIterator 类型的对象调用listIterator(list.size())函数,这里需要注意,由于是从集合中的最后元素开始向前遍历,所以这里我们对于listIterator()函数应该给一个参数,否则不会出现结果;

7)lit.hasPrevious()判断是否有元素,如果有前一个元素可以迭代,则返回 true ,lit.previous()返回列表中的前一个元素;

package cn.xuexi.list;import java.util.ArrayList;import java.util.Iterator;import java.util.List;import java.util.ListIterator;/* * List接口的特有的迭代器 */public class ListIteratorDemo { public static void main(String[] args) { //创建集合对象 List list=new ArrayList(); //向集合中添加数据 list.add("nba"); list.add("aaa"); list.add("cba"); list.add("wnba"); list.add("wcba"); //使用Collection接口中的Iterator进行迭代 System.out.println("使用Iterator进行迭代"); for (Iterator it = list.iterator(); it.hasNext();) { //输出数据 System.out.println(it.next()); } //使用List接口中的特有迭代器ListIterator进行迭代 System.out.println("使用ListIterator进行正向迭代"); for (ListIterator it = list.listIterator();it.hasNext();) { System.out.println(it.next()); } System.out.println("使用ListIterator进行逆向迭代"); //it.hasPrevious()如果为true表示集合中从后往前迭代开始有元素 for (ListIterator it = list.listIterator(list.size()); it.hasPrevious();) { //it.previous()表示返回集合中的上一个元素 System.out.println(it.previous()); } }}

注意:

1)使用ListIterator逆向迭代的时候一定要使用带有参数的listIterator(index)函数,然后集合的长度作为参数下标,否则该迭代光标就会从前往后迭代;

这里的index>=0&&index<=list.size();

2)这里为何使用list.size()作为参数而不是list.size()-1,因为我们要从集合中最后一个元素后面开始迭代,如果是list.size()-1,迭代光标会从最后一个元素前面开始,这样会导致忽略了集合中的最后一个元素;

扩展:演示在使用ListIterator迭代器遍历List集合的时候,可以对这个集合中的元素进行修改操作,代码如下:

案例:List集合的特有迭代器对象。

在使用ListIterator迭代器遍历集合的时候是可以修改集合的;

需求:迭代取出集合中的元素的时候,判断是否等于某个数据,如果相等使用迭代器对象调用迭代器中的add()函数向集合中添加一个新的数据或者使用迭代器中的set函数更改数据。

分析和步骤:

1)在这个类中使用new关键字创建ArrayList类的对象list,并赋值给接口List类型;

2)使用集合对象list调用属于Collection接口中的add(E e)函数向集合List中添加几个字符串数据;

3)使用集合对象list调用listIterator()函数生成ListIterator 类型的迭代器对象;

4)使用while循环遍历集合,将获得的数据强制转换为String类型;

5)使用判断结构判断找到的对象数据是否是集合中的一个数据,如果是,则使用迭代器对象调用add()函数向集合中添加一个新的字符串数据;

6)循环结束输出集合;

补充:注意,使用Iterator迭代器遍历集合的时候,在Iterator接口中只提供了remove()函数进行修改集合;

总结:(很重要,经常犯错误)

我们在迭代集合时,如果要对集合修改,一定要使用迭代器本身的功能!一定不能使用集合对象调用集合中的函数去修改集合。

Iterator中只提供了一个:remove功能

ListIterator中提供了:add、remove、set功能

1.3、List集合遍历方法总结

说明:

迭代List集合有多种写法,有五种写法:

1)toArray(); Object[] arr = list.toArray();

2)使用for循环借助Iterator接口可以实现;

3)使用for循环借助ListIterator接口可以实现;

4)使用普通for循环

for( int i=0;i

5)使用foreach循环:

for( Object obj : list ){ System.out.println(obj);}package cn.xuexi.list;import java.util.ArrayList;import java.util.Iterator;import java.util.List;import java.util.ListIterator;/* * 迭代集合的五种方法: * 1.toArray() * 2.使用Iterator迭代器遍历集合 * 3.使用ListIterator迭代器遍历集合 * 4.使用普通for循环遍历集合 * 5.使用foreach循环遍历集合 */public class ListIteratorDemo2 { public static void main(String[] args) { //创建集合对象 List list=new ArrayList(); //向集合添加数据 list.add("及时雨"); list.add("白麒麟"); list.add("黑旋风"); list.add("母老虎"); //使用toArray()函数遍历集合 System.out.println("使用toArray()函数遍历集合"); Object[] arr = list.toArray(); //遍历数组 for (int i = 0; i < arr.length; i++) { System.out.println(arr[i]); } System.out.println("使用Iterator迭代器遍历集合");//使用Iterator迭代器遍历集合 for (Iterator it = list.iterator(); it.hasNext();) { System.out.println(it.next()); } System.out.println("使用ListIterator迭代器遍历集合"); //使用ListIterator迭代器遍历集合 for (ListIterator it = list.listIterator(); it.hasNext();) { System.out.println(it.next()); } System.out.println("使用普通for循环遍历集合"); //使用普通for循环遍历集合 for (int i = 0; i < list.size(); i++) { System.out.println(list.get(i)); } System.out.println("使用foreach循环遍历集合"); //使用foreach循环遍历集合 for (Object obj : list) { System.out.println(obj); } }}

2、练习

需求:使用List存储字符串,并去除重复元素,要求在同一个集合中去除。

假如有一个集合,里面有一些重复的字符串。把重复给我去除。

分析和步骤:

思路:

1):创建一个空的集合l2

2):循环遍历l1,取出每个字符串

3):判断取出的元素在l2中是否存在

是:存在,舍弃

否:不存在,添加到l2

4):清空L1

5):把L2的元素添加到L1

注意:这里使用for循环遍历迭代的是l1集合,不是l2集合。

/** * 需求:去除集合中的重复元素。 */public class CollectionTest { public static void main(String[] args) {  List l1= new ArrayList();  l1.add("aaaa"); l1.add("abc"); l1.add("abc"); l1.add("bbbb"); l1.add("aaaa"); l1.add("xyz"); l1.add("xyz"); l1.add("aaaa");  //新建一个新的集合容器 List l2 = new ArrayList();  for (Iterator it = l1.iterator(); it.hasNext();) { Object obj = it.next(); //判断从原始集合中取出的元素在新的集合中是否存在 if( !l2.contains(obj) ){ l2.add(obj); } } //循环结束之后,l2集合中一定保存的都是不重复的元素 //把原始集合中的数据清空,把l2中的不重复元素添加到原始集合中 l1.clear(); l1.addAll(l2); System.out.println(l1); }}

3、LinkedList介绍(掌握)

3.1 概述

List集合|------ArrayList集合|------LinkedList集合

LinkedList集合:它也是List接口的实现类,它的底层使用的链接列表(链表)数据结构。

链表结构的特点:有头有尾。

补充概念:什么是数据结构?

数据结构:数据的存储方式,不同的集合容器它们存储数据的方式都不一样。而我们学习众多的集合容器,重点是知道每个集合存储数据的方式即可。不同集合的存储方式不同,导致集合中的数据存取,以及元素能否重复等相关操作也都不相同。

LinkedList集合它采用的是数据结构中的链表结构:

链表结构:由一个链子把多个节点连接起来的数据结构。

节点:在链表结构中每个可以保存数据的空间称为节点,而一个链表结构是由多个节点组成的。

也就是说链表结构使用节点来存储数据。

每个节点(存储数据的空间)可以分成若干部分,其中有一部分存储数据,另外一部分存储的是其他节点的地址。

说明:

1)节点:实际存储自己的数据+其他节点的地址,而作为链表结构中的最后一个节点的存储地址的空间是null。

2)链表结构查询或遍历时都是从头到尾的遍历。

3)链表结构是有头有尾的。因此LinkedList集合中定义了自己的特有的方法都是围绕链表的头和尾设计的。

4)链表分为两种:单向链表和双向链表。

如下图所示就是数组结构和链表结构对数据的查询、添加、删除的区别对比图如下所示:

总结:

1)数组结构查询快,但是增删慢;

2)链表结构查询慢,但是增删快;

上述两种数据结构快慢只是相对来说。

LinkedList集合特点:

1、它的底层使用的链表结构;

2、有头有尾,其中的方法都是围绕头和尾设计的;

3、LinkedList集合可以根据头尾进行各种操作,但它的增删效率高,查询效率低;

LinkedList集合增删效率高是因为底层是链表结构,如果增加或者删除只需要在增加或者删除节点的位置上记住新的节点的地址即可,而其他节点不需要移动,所以速度会快。

而查询遍历由于链表结构的特点,查询只能从头一直遍历到链表的结尾,所以速度会慢。

4、LinkedList集合底层也是线程不安全。效率高;

5、也可以存储null元素;

3.2 LinkedList集合的特有方法

说明:

这里只有两个构造函数,不像ArrayList集合有三个构造函数,而这里不需要给容器初始化容量大小,因为LinkedList集合的特点就是链表结构,在底层如果新增加一个元素那么前一个节点记住新增加的节点地址,而新增加的节点记住后一个节点地址就可以,什么时候增加什么记住地址即可,不需要初始化容量大小。

由于LinkedList是链表结构,而链表有头有尾,所以在LinkedList集合中专门为链表结构提供了特有的函数。

练习:需求练习LinkedList集合中特有的函数。

分析和步骤:

1)使用new关键字创建LinkedList类的对象list,并赋值为LinkedList类型;

2)使用对象list调用LinkedList集合中的addFirst()函数向集合中添加字符串;

3)利用for循环和迭代器迭代遍历LinkedList集合;

4)使用对象list调用LinkedList类中的getLast()函数获取最后一个元素并输出;

5)使用对象list调用LinkedList类中的removeFirst()函数删除第一个元素并输出;

3.3 队列和堆栈结构(面试题)

由于LinkedList集合底层使用的链表结构:导致LinkedList集合在存储数据的时候可以根据头和尾进行增、删、改、查各种操作。可以使用LinkedList集合模拟常见的2种数据结构:

队列结构:先进的先出或者后进的后出。(排队买票)

堆栈结构:先进的后出或者后进的先出。(手枪的弹夹)

经常使用LinkedList模拟上述的2种结构:

1)案例:使用LinkedList模拟队列结构 。先进的先出 (排队买票)

注意:队列的特点是获取一个元素便同时将获取的元素直接删除;

可以理解为一个人买票,买完票就不在队伍中了;

由于队列的特点,是先进先出,所以这里可以删除第一个元素,并返回删除的元素;

分析和步骤:

1)新建一个模拟队列的类QueueDemo;

2)在这个类Queue中创建LinkedList类的集合对象list;

3)定义一个添加元素的函数addElement(Object obj),在函数体里面使用集合对象list调用LinkedList类的集合中的addLast(obj)函数,将元素添加尾部;

4)定义一个获取元素的函数getElement(),返回值是Object,由于元素获取之后就要从集合中删除,所以在函数体里面使用集合对象list调用LinkedList类的集合中的removeFirst()函数,移除头部元素并返回删除的元素;

5)定义一个判断队列中是否还有元素存在的函数isNull(),函数返回值是boolean,在函数体里面使用集合对象list调用LinkedList类的集合中的isEmpty()函数,不包含元素返回true,包含返回false;

6)定义一个测试类LinkedListDemo1,在这个类中使用new关键字创建刚才创建好的Queue类的对象q;

7)使用对象q调用addElement()函数向集合中添加数据;

8)使用while循环依次取出集合中的数据,对象q调用isNull()函数判断是否还有元素,对象q调用getElement(),获得元素输出并打印;

package cn.xuexi.list.test;import java.util.LinkedList;/* * 模拟队列结构 特点:先进先出,类似买票 *///创建模拟队列的类class QueueDemo{ //创建LinkedList集合对象 LinkedList list=new LinkedList(); //定义函数模拟向队列中添加元素 public void addElement(Object obj) { //每次都向集合最后面添加数据 添加到链表的尾部 list.addLast(obj); } //定义函数,让外界获取元素 public Object getElement() { /* * 由于队列的特点是获取一个元素便同时将获取的元素直接删除 * 可以理解为一个人买票,买完票就不在队伍中了 * 由于队列的特点,是先进先出,所以这里可以删除第一个元素,并返回删除的元素 */ return list.removeFirst(); } //判断队列中是否还有元素存在 public boolean isNull() { /* * 在LinkedList函数中虽然没有判断集合中是否还含有数据 * 但是它的接口List中含有,所以我们可以使用list.isEmpty()来判断集合中是否还含有数据 * isEmpty()函数是判断集合中没有元素返回true,有元素返回false */ return list.isEmpty(); }}public class LinkedListQueue { public static void main(String[] args) {//创建模拟队列类的对象 QueueDemo q=new QueueDemo(); //向队列中添加数据 q.addElement("元素1"); q.addElement("元素2"); q.addElement("元素3"); //判断集合中是否还含有元素,有,则输出数据 while(!q.isNull())//!q.isNull()如果为true,表示集合中还有数据 { //说明集合中还有元素,取出数据 输出元素结果 "元素1" "元素2" "元素3"  System.out.println(q.getElement()); } }}

2)案例:使用LinkedList模拟堆栈结构。先进的后出,(手枪的弹夹)

代码和上述相同,只是将QueueDemo类中的getElement()函数体中的代码改成

return list.removeLast()即可

removeLast()表示移除尾部元素并返回删除的元素。

package cn.xuexi.list.test;import java.util.LinkedList;/* * 模拟堆栈结构 特点:先进后出,类似子弹弹夹 *///创建模拟堆栈的类class QueueDemo{ //创建LinkedList集合对象 LinkedList list=new LinkedList(); //定义函数模拟向堆栈中添加元素 public void addElement(Object obj) { //每次都向集合最后面添加数据 添加到链表的尾部 list.addLast(obj); } //定义函数,让外界获取元素 public Object getElement() { /* * 由于堆栈的数据结构的特点是先进后出, * 所以我们可以将最后添加的数据先移除 */ return list.removeLast(); } //判断堆栈中是否还有元素存在 public boolean isNull() { /* * 在LinkedList函数中虽然没有判断集合中是否还含有数据 * 但是它的接口List中含有,所以我们可以使用list.isEmpty()来判断集合中是否还含有数据 * isEmpty()函数是判断集合中没有元素返回true,有元素返回false */ return list.isEmpty(); }}public class LinkedListQueue { public static void main(String[] args) { //创建模拟堆栈类的对象 QueueDemo q=new QueueDemo(); //向堆栈中添加数据 q.addElement("元素1"); q.addElement("元素2"); q.addElement("元素3"); //判断集合中是否还含有元素,有,则输出数据 while(!q.isNull())//!q.isNull()如果为true,表示集合中还有数据 { //说明集合中还有元素,取出数据 输出元素结果 "元素3" "元素2" "元素1"  System.out.println(q.getElement()); } }}

4、Vector介绍(了解)

Vector集合是JDK1.0的时候出现的集合,它在jdk1.2的时候被收编到List接口的下面。而这个集合被JDK1.2中的ArrayList集合代替。

演示Vector类的函数:

分析和步骤:

1)定义一个测试类VectorDemo,并创建这个类的对象v;

2)使用对象v调用Vector类中的addElement()函数向Vector集合中添加字符串数据;

3)使用集合对象v调用iterator()函数获得迭代器的对象it;

4)使用循环借助迭代器对象调用hasNext()函数和next()函数遍历集合,并输出,可是iterator()不是Vector集合原来就开始使用的迭代方式;

5)使用对象v调用Vector类中的elements()函数来获得类似迭代器的对象en,并给Enumeration类型;

6)同样使用en对象调用Enumeration中的hasMoreElements()和nextElement()函数借助集合进行遍历,而这种方式是最开始诞生Vector类使用迭代的方式;

问题:

通过查阅API得知,Vector集合从jdk1.0版本就已经存在了,而迭代器Iterator是从jdk1.2版本开始才引入的,那么在jdk1.2版本之前是怎么对Vector集合进行迭代和遍历取出集合中的数据呢?

解释说明:

1)在1.2版本之前我们借助于另一个接口Enumeration来实现迭代的;

2)使用接口Enumeration对集合进行迭代,可以先使用Vector集合的对象调用Vector集合中的elements()函数来获得接口Enumeration的对象;

3)然后使用接口Enumeration的对象调用hasMoreElements()判断集合中是否还有元素,有返回true;

4)然后使用接口Enumeration的对象调用nextElement()函数获取集合中的元素;

package cn.xuexi.vector;import java.util.Enumeration;import java.util.Iterator;import java.util.Vector;/* * Vector集合的演示 */public class VectorDemo { public static void main(String[] args) { //创建Vector集合对象 Vector v=new Vector(); //向Vector集合中添加数据 v.addElement("黑旋风"); v.addElement("刘德华"); v.addElement("成龙"); v.addElement("李连杰"); //遍历集合 /*for (Iterator it = v.iterator(); it.hasNext();)  { //获取集合里面的数据 System.out.println(it.next()); }*/ //使用jdk1.2版本之前对Vector集合进行遍历 for (Enumeration en = v.elements(); en.hasMoreElements();) { //输出集合中的数据 System.out.println(en.nextElement()); } }}

Vector集合它就是ArrayList集合,可以使用Enumeration迭代Vector集合,但是由于Enumeration迭代器中的方法的名字太长,被Iterator代替。后期如果需要迭代器Vector应该优先考虑使用Iterator迭代。

Vector集合的特点:

1)Vector是jdk1.0出现的集合,它的增删,查询效率都比较低;

2)由于Vector底层是线程安全的。ArrayList 的底层是不安全的,因此ArrayList各方面的效率都比Vector高。

3)对于Vector类现在开发中已经几乎不再使用。根本就不用。

5、List下的子类总结(掌握)

 Collection集合(接口) |----List集合(接口):可以存储重复元素、可以存储null、有角标、存取有序。 |----ArrayList集合(类):实现List接口。ArrayList集合中的特有方法是实现List 底层使用可变数组结构。 查询遍历的效率比较高、增删的效率比较低 属于线程不安全的集合类。执行效率比较高 |----LinkedList集合(类):实现List接口。 底层使用链表结构。(链表:有头有尾) LinkedList集合中的特有方法都是围绕链表的头尾设计 查询遍历的效率比较慢、增删的效率比较高 属于线程不安全的集合类。执行效率比较高 |----Vector集合(类):实现List接口。线程安全的集合类。 底层使用可变数组结构。查询遍历效率、增删效率都比较低。 Vector类属于线程安全的集合类。效率比较慢。现在开发中已经不再使用。

问题:遇到对线程有需求的情况,应该使用哪个集合类?

还使用LinkedList、ArrayList(在后面学习过程中,可以解决LinkedList ArrayList线程不安全的问题)

疑问:

1)解释:为什么ArrayList集合增删效率低,而查询速度快?

因为我们向集合中添加元素的时候,有时会将元素添加到集合中的最前面,或者有可能删除最前面的数据,这样就导致其他数据向后移动或者删除时向前移动,所以效率会低。

对于查询ArrayList集合,由于ArrayList集合是数组结构,而数组结构是排列有序的,并且下标是有序增加的,当查询ArrayList集合的时候可以按照排列顺序去查询,或者直接可以通过某个下标去查询,这样就会导致查询速度相对来说会快很多。

2)解释:为什么LinkedList集合增删效率快,而查询速度慢?

LinkedList集合增删效率高是因为底层是链表结构,如果增加或者删除只需要在增加或者删除节点的位置上记住新的节点的地址即可,而其他节点不需要移动,所以速度会快。

而查询遍历由于链表结构的特点,查询只能从头一直遍历到链表的结尾,所以速度会慢。

注意:学习了这么多集合类,在开发中如果不知道使用哪个集合,到底什么时候使用ArrayList集合和LinkedList集合?

1)如果对集合进行查询操作建议使用ArrayList集合;

2)如果对集合进行增删操作建议使用LinkedList集合;

总结:如果实在把握不好使用的时机,建议大家以后在开发中都使用ArrayList集合即可,因为在开发中我们对于集合的操作几乎都是查询操作,很少执行增删操作的。

6、Set接口

6.1、Set接口概述

Collection集合(接口)的接口下面有2个直接的子接口: |-----List集合(接口):可以保存重复元素,拥有下标,存储有序,可以存储多个null元素。 |-----ArrayList类:底层是可变数组,根据下标进行操作,查询效率快,增删效率低。 |-----LinkedList类:底层是链表,根据链表的头尾进行操作,增删效率快,查询效率低。 |-----Set集合(接口):不能保存重复元素,没有下标。可以存储null但只能有一个。并且不保证存取的顺序,也就是说对于集合set进行存取操作的时候都没有任何顺序,没有任何规律而言。 |-----HashSet类 |-----LinkedHashSet类 |-----TreeSet类

说明:

1)Set接口中没有自己的特有函数,所有的函数全部来自于Collection接口。

2)Set集合没有角标,只能通过Iterator迭代器遍历获取集合中的元素,Set集合不能使用ListIterator迭代器,因为ListIterator只是针对List集合特有的迭代器。

6.2、Set接口的练习

需求:存储字符串并遍历。

由于Set是接口不能创建对象,只能创建Set实现类的接口,任何一个都可以,这里就创建HashSet类的对象。

分析和步骤:

1)使用new关键字创建Set接口下的子类HashSet类的对象,并赋值给对象s,类型是Set接口类型;

2)使用对象s调用add()函数向集合中添加字符串数据;

3)循环遍历集合;

package cn.xuexi.set;import java.util.HashSet;import java.util.Iterator;import java.util.Set;/* * Set集合的练习 */public class SetDemo { public static void main(String[] args) { //创建集合对象 Set s=new HashSet();  //向Set集合中添加数据 s.add("aaa"); s.add("aaa"); s.add("bbb"); s.add(null); s.add("ccc"); //遍历集合 //注意:由于Set集合中不允许包含重复的元素,所以当添加元素的时候, //集合对象会把重复的元素删除,不让保存到集合中 for (Iterator it = s.iterator(); it.hasNext();) { //输出数据 System.out.println(it.next()); } }}

7、HashSet集合(掌握)

7.1、HashSet类的介绍和特点

说明:

1)实现了Set接口,具备了Set集合的特性;

2)不保证集合中的迭代顺序(不保证元素存取一致),允许存储null元素;

3)底层使用哈希表结构;

案例:HashSet集合的应用。

分析和步骤:

1)使用new关键字创建HashSet集合类的对象set,类型是HashSet类型;

2)使用集合对象set调用集合中的add()函数向HashSet集合中添加字符串数据;

3)使用对象set调用iterator()函数获取迭代器对象it;

4)使用for循环遍历,通过迭代器对象调用hasNext()和next()函数,并输出获取的数据;

package cn.xuexi.set;import java.util.HashSet;import java.util.Iterator;/* * hashSet演示 */public class HashSetDemo { public static void main(String[] args) { //创建HashSet集合对象 HashSet set=new HashSet(); //向集合中添加数据 set.add("JavaSe"); set.add("JavaSE"); set.add("JavaEE"); set.add("AAAA"); set.add("AAAA"); set.add("bbbb"); set.add("bbbb"); //遍历集合 for (Iterator it = set.iterator(); it.hasNext();) { //输出集合中的数据 System.out.println(it.next()); } }}

输出结果:

通过以上程序输出结果得出结论:

1)HashSet集合不能存储重复的元素;

2)HashSet集合存储元素的顺序不固定;

接下我们要分析为什么HashSet集合存储的数据顺序不固定和为什么不支持存储重复的元素?

答案肯定和HashSet集合的底层哈希表数据结构有关系,所以接下来我们要学习什么是哈希表。

7.2、哈希表介绍(掌握)

哈希表:

它是一个数据结构,底层依赖的是数组,只是不按照数组的下标操作数组中的元素。需要根据数组中存储的元素的哈希值进行元素操作。

哈希表的存储过程:

1)哈希表底层是一个数组,我们必须知道集合中的一个对象元素到底要存储到哈希表中的数组的哪个位置,也就是需要一个下标。

2)哈希表会根据集合中的每个对象元素的内容计算得出一个整数值。由于集合中的对象元素类型是任意的,而现在这里使用的算法必须是任意类型的元素都可以使用的算法。能够让任意类型的对象元素都可以使用的算法肯定在任意类型的对象所属类的父类中,即上帝类Object中,这个算法就是Object类中的hashCode()函数。

结论:要给HashSet集合中保存对象,需要调用对象的hashCode函数。

解释说明:

通过查阅API得知,使用Object的任意子类对象都可以调用Object类中的hashCode()函数并生成任意对象的哈希码值。

代码演示如下:

输出结果:

由以上结果可以看出任意对象调用Object类中的hashCode()函数都会生成一个整数。

3)hashCode算法,得到一个整数,但是这个整数太大了,这个值不能直接作为数组下标的。所以底层还会对这个值结合数组的长度继续计算运行,得到一个在0~数组长度-1之间的整数,这样就可以作为数组的下标了。

问题1:使用hashCode函数生成的一个过大整数是用什么算法将将生成哈希码值变成0~数组长度-1之间的数字呢?

其中一种最简单的算法是可以实现的,举例:假设底层哈希表中数组长度是5,那么下标的范围是0~4,

所以我们这里可以使用生成的哈希码值(过大的整数)对数组长度取余数,我们发现任何数在这里对5取余都是在 0 1 2 3 4 之间,所以这样就可以获取到0~数组长度-1之间的下标了。

问题2:如果数据过多,HashSet底层的数组存储不下,怎么办?

hashSet集合底层数组初始容量是16,如果大小不够,那么会继续新创建一个数组,新数组大小等于原来数组的大小*0.75+原来数组的大小。

4)如果上述做法已经计算出底层数组的下标位置,那么就要判断计算出的下标位置是否已经有元素了:

A.如果下标对应的位置没有元素:直接存储数据;

B.如果下标对应的位置有元素:这时就必须调用对象的equals()函数比较两个对象是否相同:

如果结果是true:相同,直接将要添加的数据丢弃;

如果结果是false:不相同,那直接将数据存储到数组当前空间位置;

但是当前位置已经存在元素了,怎么将后来的数据存储到数组中呢?

这里需要使用类似链表的结构了,在当前位置上在画出来一个空间,然后将当前的对象数据保存到新划出来的空间中,在原来的空间中设置一个引用变量记录着新划分空间的地址,如果后面还有数据要存储当前空间,做法和上述相同。

哈希表如下图所示:

最终结论:哈希表底层通过hashCode和equals算法结合,来保证对象数据在HashSet集合中不重复唯一,并且存储的顺序不固定。

哈希表:数组+hashCode函数+equals函数,哈希表保证对象唯一需要依赖对象的hashCode和equals方法。

面试题:哈希表如何保证元素唯一?

哈希表保证元素唯一依赖两个方法:hashCode和equals。

哈希表底层其实还是一个数组,元素在存储的时候,会先通过hashCode算法结合数组长度得到一个索引。然后判断该索引位置是否有元素:如果没有,不用调用equals函数,直接存储;如果有,再调用元素的equals方法比较是否相同:相同,直接舍弃;如果不同,也存储。

7.3、HashSet保存自定义对象

案例需求:向HashSet集合中添加自定义对象。

分析和步骤:

1)定义一个Person类,在Person类中定义两个属性name和age,并分别生成toString()、set()、get()方法;

2)在定义一个测试类HashSetDemo,在这个类中的main函数中,使用new关键字创建HashSet集合类的对象set,类型是HashSet类型;

3)使用集合对象set调用集合中的add()函数向HashSet集合中添加Person类的匿名对象,并通过创建Person类的对象调用构造函数给name和age赋值;

4)使用对象set调用iterator()函数获取迭代器对象it;

5)使用for循环遍历,通过迭代器对象调用hasNext()和next()函数,并输出获取的数据;

测试类代码如下:

package cn.xuexi.set;import java.util.HashSet;import java.util.Iterator;/* * 使用哈希表存储自定义对象 */public class HashSetDemo2 { public static void main(String[] args) { //创建集合对象 HashSet set=new HashSet(); //向集合中添加自定义对象数据 set.add(new Person("黑旋风

list集合下标从几开始_Java基础进阶 集合框架详解相关推荐

  1. java基础与案例开发详解 pdf下载_Java基础与案例开发详解(张孝祥) pdf_IT教程网...

    资源名称:Java基础与案例开发详解(张孝祥) pdf 第1.2和3章介绍Java环境的安装配置,Java程序的开发过程和Java语言的基础语法. 第4章介绍数组和常用算法的相关知识. 第5章和6章介 ...

  2. 反射 数据类型_Java基础:反射机制详解

    一.什么是反射: (1)Java反射机制的核心是在程序运行时动态加载类并获取类的详细信息,从而操作类或对象的属性和方法.本质是JVM得到class对象之后,再通过class对象进行反编译,从而获取t对 ...

  3. java代码讲解_Java基础系列-代码块详解

    注:本文出自博主 Chloneda:个人博客 | 博客园 | Github | Gitee | 知乎 前言 Java基础系列,尽量采用通俗易懂.循序渐进的方式,让大家真正理解Java基础知识! 代码块 ...

  4. java object 详解_Java基础之Object类详解

    类Object是类层次结构的根类.每个类都直接或者间接地继承Object类.所有对象(包括数组)都实现这个类的方法.Object类中的构造方法只有一个,并且是无参构造方法,这说明每个类中默认的无参构造 ...

  5. java多线程教程_java 基础教程之多线程详解及简单实例

    java 多线程详解 在这篇文章里,我们关注多线程.多线程是一个复杂的话题,包含了很多内容,这篇文章主要关注线程的基本属性.如何创建线程.线程的状态切换以及线程通信. 线程是操作系统运行的基本单位,它 ...

  6. 【Java基础】HashMap原理详解

    [Java基础]HashMap原理详解 HashMap的实现 1. 数组 2.线性链表 3.红黑树 3.1概述 3.2性质 4.HashMap扩容死锁 5. BATJ一线大厂技术栈 HashMap的实 ...

  7. 集合框架详解之Set、Map接口与实现类

    集合框架 1.Set集合 1.1Set集合概述 Set实现类:HashSet类.TreeSet类 特点:无序.无下标.元素不可重复 方法:全部继承自Collection中的方法 1.11 Set接口的 ...

  8. Android基础总结: Camera2详解之一 API学习

    Camera2的API出来有些年头了,只是赶项目多次使用,没时间好好总结,年终了,正好结合google的官方Camera2demo 和开发中使用的情况,做个详细梳理,研究总结之后,才发现Camera2 ...

  9. Java 集合框架 详解

    一.Java 集合框架概述 集合框架是一个用来代表和操纵集合的统一架构(java集合框架位于java.util包中).所有的集合框架都包含如下内容: 接口:是代表集合的抽象数据类型.例如 Collec ...

最新文章

  1. mysql表和表的关系_mysql表与表之间建关系
  2. php 实现 html转js
  3. linux添加自己的库,Linux学习笔记——例叙makefile 增加自定义共享库
  4. not in the sudoers file. This incident will be reported.
  5. 互联网移动支付技术_安全架构图(安全技术/安全协议/加密技术)——转载图片...
  6. ArrayList使用内存映射文件
  7. 【算法设计与分析】10 差消法化简高阶递推方程
  8. 第二十九天-ssh服务重要知识深入浅出讲解
  9. java加载properties文件的几种方式,java高级面试笔试题
  10. mac电脑LC_CTYPE: cannot change locale (UTF-8): No such file or directory
  11. java批处理框架 pdf_史上最轻松入门之Spring Batch - 轻量级批处理框架实践
  12. Android屏幕尺寸(来自网络整理)
  13. 常用大部分渗透工具下载
  14. 计算机专业需要外语口试,计算机专业英语词汇大全,计算机专业必读英语词汇!...
  15. 教老婆学python
  16. 并发安全的计数统计类:AtomicLong和LongAdder
  17. 一年365天,把1.0作为每天的能力值基础,每天原地踏步 则能力值为1.0,如果每天努力一点点则能力值提高1%,每天再努力一点则能力值提高2%,那一年后,这3种行为收获的成果相差多少呢?
  18. spark streamming + kafka + Redis 实践
  19. 物联网连接拼图:蜂窝与非蜂窝
  20. 论文阅读笔记:(2021.06, cvpr) Monocular 3D Object Detection: An Extrinsic Parameter Free Approach

热门文章

  1. 嵌入式电路设计(最小系统)
  2. 随想录(基于内存映射的进程通信)
  3. 一步一步写算法(之哈夫曼树 下)
  4. jsp mysql 连接池_Tomcat下JSP连接mysql连接池
  5. wordpress连接mysql数据库_安装wordpress连接mysql数据库问题
  6. linux oracle 运维_oracle数据库常用命令整理
  7. ios html异步加载图片,iOS 异步加载本地图片
  8. java rsa enc 源码_RSA加解密源码 | 学步园
  9. java递归查询无限极分类_sqlserver实现树形结构递归查询(无限极分类)的方法
  10. python好用的模块_python常用的内置模块和常用的第三方模块