热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

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

今日任务1、List接口介绍(掌握常用List特有方法)2、练习3、ArrayList介绍(必须清楚集合的特征、掌握集合中的方法)4、LinkedList介绍(必须清楚集合的特征、

今日任务

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接口。

58aac4a9461f3601cfc35f9f29d86c2b.png

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 >&#61; 0 && index <&#61; size()&#xff1b;

61a6d13d29bd0f563becc7099a1305fd.png

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

index >&#61; 0 && index <&#61; size()&#xff1b;

222de48ffad9abd113a0d3abb41ef225.png

分析和步骤&#xff1a;

1)定义一个ListDemo类&#xff0c;在这个类中定义一个method_1()函数&#xff1b;

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

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

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

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

public static void method_1() { // 创建集合对象 List list &#61; new ArrayList(); List list1 &#61; 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); }

注意&#xff1a;

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

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

2)获取方法&#xff1a;

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

731d0df065ea6f54eceb4079b3bc6508.png

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

986dd5024758e0b06237ead620863d0d.png

分析和步骤&#xff1a;

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

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

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

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

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

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

注意&#xff1a; 下标的范围&#xff1a;index >&#61; 0 && index <&#61; size()-1&#xff1b;

1.2、List的特有迭代器

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

ListIterator接口&#xff1a;

5ea092eba0da5bf17ca9ada94d0954ca.png

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

注意&#xff1a;ListIterator迭代器只能被List集合使用&#xff0c;其它集合都不能使用。

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

1e4a0e317604bfe218b3898042a50c2b.png

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

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

分析和步骤&#xff1a;

1)定义一个ListIteratorDemo类&#xff1b;

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

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

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

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

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

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

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&#61;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 &#61; list.iterator(); it.hasNext();) { //输出数据 System.out.println(it.next()); } //使用List接口中的特有迭代器ListIterator进行迭代 System.out.println("使用ListIterator进行正向迭代"); for (ListIterator it &#61; list.listIterator();it.hasNext();) { System.out.println(it.next()); } System.out.println("使用ListIterator进行逆向迭代"); //it.hasPrevious()如果为true表示集合中从后往前迭代开始有元素 for (ListIterator it &#61; list.listIterator(list.size()); it.hasPrevious();) { //it.previous()表示返回集合中的上一个元素 System.out.println(it.previous()); } }}

注意&#xff1a;

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

6f56ceb22b8622c3c00f0428df9d738f.png

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

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

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

案例&#xff1a;List集合的特有迭代器对象。

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

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

分析和步骤&#xff1a;

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

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

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

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

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

6)循环结束输出集合&#xff1b;

2850fab2de7aafa919288d21668822fb.png

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

总结&#xff1a;(很重要&#xff0c;经常犯错误)

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

Iterator中只提供了一个&#xff1a;remove功能

ListIterator中提供了&#xff1a;add、remove、set功能

1.3、List集合遍历方法总结

说明&#xff1a;

迭代List集合有多种写法&#xff0c;有五种写法&#xff1a;

1)toArray(); Object[] arr &#61; list.toArray();

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

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

4)使用普通for循环

for( int i&#61;0;i

5)使用foreach循环&#xff1a;

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;/* * 迭代集合的五种方法&#xff1a; * 1.toArray() * 2.使用Iterator迭代器遍历集合 * 3.使用ListIterator迭代器遍历集合 * 4.使用普通for循环遍历集合 * 5.使用foreach循环遍历集合 */public class ListIteratorDemo2 { public static void main(String[] args) { //创建集合对象 List list&#61;new ArrayList(); //向集合添加数据 list.add("及时雨"); list.add("白麒麟"); list.add("黑旋风"); list.add("母老虎"); //使用toArray()函数遍历集合 System.out.println("使用toArray()函数遍历集合"); Object[] arr &#61; list.toArray(); //遍历数组 for (int i &#61; 0; i

2、练习

需求&#xff1a;使用List存储字符串&#xff0c;并去除重复元素&#xff0c;要求在同一个集合中去除。

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

分析和步骤&#xff1a;

思路&#xff1a;

1)&#xff1a;创建一个空的集合l2

2)&#xff1a;循环遍历l1&#xff0c;取出每个字符串

3)&#xff1a;判断取出的元素在l2中是否存在

是&#xff1a;存在&#xff0c;舍弃

否&#xff1a;不存在&#xff0c;添加到l2

4)&#xff1a;清空L1

5)&#xff1a;把L2的元素添加到L1

注意&#xff1a;这里使用for循环遍历迭代的是l1集合&#xff0c;不是l2集合。

/** * 需求&#xff1a;去除集合中的重复元素。 */public class CollectionTest { public static void main(String[] args) { List l1&#61; 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 &#61; new ArrayList(); for (Iterator it &#61; l1.iterator(); it.hasNext();) { Object obj &#61; it.next(); //判断从原始集合中取出的元素在新的集合中是否存在 if( !l2.contains(obj) ){ l2.add(obj); } } //循环结束之后&#xff0c;l2集合中一定保存的都是不重复的元素 //把原始集合中的数据清空&#xff0c;把l2中的不重复元素添加到原始集合中 l1.clear(); l1.addAll(l2); System.out.println(l1); }}

3、LinkedList介绍(掌握)

3.1 概述

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

0fe3cbf8111a2b5f9961d8173e7313ef.png

LinkedList集合&#xff1a;它也是List接口的实现类&#xff0c;它的底层使用的链接列表(链表)数据结构。

链表结构的特点&#xff1a;有头有尾。

补充概念&#xff1a;什么是数据结构&#xff1f;

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

LinkedList集合它采用的是数据结构中的链表结构&#xff1a;

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

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

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

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

说明&#xff1a;

1)节点&#xff1a;实际存储自己的数据&#43;其他节点的地址&#xff0c;而作为链表结构中的最后一个节点的存储地址的空间是null。

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

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

4)链表分为两种&#xff1a;单向链表和双向链表。

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

99467051383dadb854fec7b8f31ab9dd.png

总结&#xff1a;

1)数组结构查询快&#xff0c;但是增删慢&#xff1b;

2)链表结构查询慢&#xff0c;但是增删快&#xff1b;

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

LinkedList集合特点&#xff1a;

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

2、有头有尾&#xff0c;其中的方法都是围绕头和尾设计的&#xff1b;

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

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

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

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

5、也可以存储null元素&#xff1b;

3.2 LinkedList集合的特有方法

30704cc428208ebf36da204263567050.png

说明&#xff1a;

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

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

3366d0a28da02a91e94404caa876b423.png
708be3c1de633c9d8135dd73236ec035.png
a103472db868a68720d5004ec3a22649.png
a1cc944b7aa3b0983e808a359ec843e8.png

练习&#xff1a;需求练习LinkedList集合中特有的函数。

分析和步骤&#xff1a;

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

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

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

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

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

f961b8f9c2f192a3780e78a730d1fff7.png

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

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

队列结构&#xff1a;先进的先出或者后进的后出。(排队买票)

堆栈结构&#xff1a;先进的后出或者后进的先出。(手枪的弹夹)

经常使用LinkedList模拟上述的2种结构&#xff1a;

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

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

可以理解为一个人买票&#xff0c;买完票就不在队伍中了&#xff1b;

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

分析和步骤&#xff1a;

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

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

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

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

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

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

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

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

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

2)案例&#xff1a;使用LinkedList模拟堆栈结构。先进的后出&#xff0c;(手枪的弹夹)

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

return list.removeLast()即可

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

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

4、Vector介绍(了解)

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

eeba0a1d083070e55b9d035bd6e58e67.png

演示Vector类的函数&#xff1a;

分析和步骤&#xff1a;

1)定义一个测试类VectorDemo&#xff0c;并创建这个类的对象v&#xff1b;

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

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

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

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

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

问题&#xff1a;

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

解释说明&#xff1a;

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

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

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

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

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&#61;new Vector(); //向Vector集合中添加数据 v.addElement("黑旋风"); v.addElement("刘德华"); v.addElement("成龙"); v.addElement("李连杰"); //遍历集合 /*for (Iterator it &#61; v.iterator(); it.hasNext();) { //获取集合里面的数据 System.out.println(it.next()); }*/ //使用jdk1.2版本之前对Vector集合进行遍历 for (Enumeration en &#61; v.elements(); en.hasMoreElements();) { //输出集合中的数据 System.out.println(en.nextElement()); } }}

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

Vector集合的特点&#xff1a;

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

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

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

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

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

问题&#xff1a;遇到对线程有需求的情况&#xff0c;应该使用哪个集合类&#xff1f;

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

疑问&#xff1a;

1)解释&#xff1a;为什么ArrayList集合增删效率低&#xff0c;而查询速度快&#xff1f;

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

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

2)解释&#xff1a;为什么LinkedList集合增删效率快&#xff0c;而查询速度慢&#xff1f;

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

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

注意&#xff1a;学习了这么多集合类&#xff0c;在开发中如果不知道使用哪个集合&#xff0c;到底什么时候使用ArrayList集合和LinkedList集合&#xff1f;

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

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

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

6、Set接口

6.1、Set接口概述

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

d416ab9cacd8dbff68767252313f8dae.png

说明&#xff1a;

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

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

6.2、Set接口的练习

需求&#xff1a;存储字符串并遍历。

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

分析和步骤&#xff1a;

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

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

3)循环遍历集合&#xff1b;

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&#61;new HashSet(); //向Set集合中添加数据 s.add("aaa"); s.add("aaa"); s.add("bbb"); s.add(null); s.add("ccc"); //遍历集合 //注意&#xff1a;由于Set集合中不允许包含重复的元素&#xff0c;所以当添加元素的时候&#xff0c; //集合对象会把重复的元素删除&#xff0c;不让保存到集合中 for (Iterator it &#61; s.iterator(); it.hasNext();) { //输出数据 System.out.println(it.next()); } }}

7、HashSet集合(掌握)

7.1、HashSet类的介绍和特点

543bfaeeaffd20bc097210c1dea62310.png

说明&#xff1a;

1)实现了Set接口&#xff0c;具备了Set集合的特性&#xff1b;

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

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

案例&#xff1a;HashSet集合的应用。

分析和步骤&#xff1a;

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

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

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

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

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&#61;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 &#61; set.iterator(); it.hasNext();) { //输出集合中的数据 System.out.println(it.next()); } }}

输出结果&#xff1a;

e389fa190753699a03b434eee9560e32.png

通过以上程序输出结果得出结论&#xff1a;

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

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

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

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

7.2、哈希表介绍(掌握)

哈希表&#xff1a;

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

哈希表的存储过程&#xff1a;

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

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

结论&#xff1a;要给HashSet集合中保存对象&#xff0c;需要调用对象的hashCode函数。

解释说明&#xff1a;

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

be1d358d8b9ae32d6c8f42e46813e1f4.png

代码演示如下&#xff1a;

5522137e3f20173e755914d886e40b52.png

输出结果&#xff1a;

64c0d7bc3d5577dc119ac368719efa01.png

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

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

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

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

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

问题2&#xff1a;如果数据过多&#xff0c;HashSet底层的数组存储不下&#xff0c;怎么办&#xff1f;

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

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

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

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

如果结果是true&#xff1a;相同&#xff0c;直接将要添加的数据丢弃&#xff1b;

如果结果是false&#xff1a;不相同&#xff0c;那直接将数据存储到数组当前空间位置&#xff1b;

但是当前位置已经存在元素了&#xff0c;怎么将后来的数据存储到数组中呢&#xff1f;

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

哈希表如下图所示&#xff1a;

8959961b19a0a967e1333dd91eb69fc5.png

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

哈希表&#xff1a;数组&#43;hashCode函数&#43;equals函数&#xff0c;哈希表保证对象唯一需要依赖对象的hashCode和equals方法。

面试题&#xff1a;哈希表如何保证元素唯一&#xff1f;

哈希表保证元素唯一依赖两个方法&#xff1a;hashCode和equals。

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

7.3、HashSet保存自定义对象

案例需求&#xff1a;向HashSet集合中添加自定义对象。

分析和步骤&#xff1a;

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

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

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

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

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

测试类代码如下&#xff1a;

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


推荐阅读
  • SpringBoot uri统一权限管理的实现方法及步骤详解
    本文详细介绍了SpringBoot中实现uri统一权限管理的方法,包括表结构定义、自动统计URI并自动删除脏数据、程序启动加载等步骤。通过该方法可以提高系统的安全性,实现对系统任意接口的权限拦截验证。 ... [详细]
  • Java容器中的compareto方法排序原理解析
    本文从源码解析Java容器中的compareto方法的排序原理,讲解了在使用数组存储数据时的限制以及存储效率的问题。同时提到了Redis的五大数据结构和list、set等知识点,回忆了作者大学时代的Java学习经历。文章以作者做的思维导图作为目录,展示了整个讲解过程。 ... [详细]
  • 本文介绍了如何使用python从列表中删除所有的零,并将结果以列表形式输出,同时提供了示例格式。 ... [详细]
  • Java学习笔记之面向对象编程(OOP)
    本文介绍了Java学习笔记中的面向对象编程(OOP)内容,包括OOP的三大特性(封装、继承、多态)和五大原则(单一职责原则、开放封闭原则、里式替换原则、依赖倒置原则)。通过学习OOP,可以提高代码复用性、拓展性和安全性。 ... [详细]
  • 集合的遍历方式及其局限性
    本文介绍了Java中集合的遍历方式,重点介绍了for-each语句的用法和优势。同时指出了for-each语句无法引用数组或集合的索引的局限性。通过示例代码展示了for-each语句的使用方法,并提供了改写为for语句版本的方法。 ... [详细]
  • 重入锁(ReentrantLock)学习及实现原理
    本文介绍了重入锁(ReentrantLock)的学习及实现原理。在学习synchronized的基础上,重入锁提供了更多的灵活性和功能。文章详细介绍了重入锁的特性、使用方法和实现原理,并提供了类图和测试代码供读者参考。重入锁支持重入和公平与非公平两种实现方式,通过对比和分析,读者可以更好地理解和应用重入锁。 ... [详细]
  • 本文比较了eBPF和WebAssembly作为云原生VM的特点和应用领域。eBPF作为运行在Linux内核中的轻量级代码执行沙箱,适用于网络或安全相关的任务;而WebAssembly作为图灵完备的语言,在商业应用中具有优势。同时,介绍了WebAssembly在Linux内核中运行的尝试以及基于LLVM的云原生WebAssembly编译器WasmEdge Runtime的案例,展示了WebAssembly作为原生应用程序的潜力。 ... [详细]
  • 本文分享了一个关于在C#中使用异步代码的问题,作者在控制台中运行时代码正常工作,但在Windows窗体中却无法正常工作。作者尝试搜索局域网上的主机,但在窗体中计数器没有减少。文章提供了相关的代码和解决思路。 ... [详细]
  • javascript  – 概述在Firefox上无法正常工作
    我试图提出一些自定义大纲,以达到一些Web可访问性建议.但我不能用Firefox制作.这就是它在Chrome上的外观:而那个图标实际上是一个锚点.在Firefox上,它只概述了整个 ... [详细]
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
  • XML介绍与使用的概述及标签规则
    本文介绍了XML的基本概念和用途,包括XML的可扩展性和标签的自定义特性。同时还详细解释了XML标签的规则,包括标签的尖括号和合法标识符的组成,标签必须成对出现的原则以及特殊标签的使用方法。通过本文的阅读,读者可以对XML的基本知识有一个全面的了解。 ... [详细]
  • Tomcat/Jetty为何选择扩展线程池而不是使用JDK原生线程池?
    本文探讨了Tomcat和Jetty选择扩展线程池而不是使用JDK原生线程池的原因。通过比较IO密集型任务和CPU密集型任务的特点,解释了为何Tomcat和Jetty需要扩展线程池来提高并发度和任务处理速度。同时,介绍了JDK原生线程池的工作流程。 ... [详细]
  • 个人学习使用:谨慎参考1Client类importcom.thoughtworks.gauge.Step;importcom.thoughtworks.gauge.T ... [详细]
  • 前景:当UI一个查询条件为多项选择,或录入多个条件的时候,比如查询所有名称里面包含以下动态条件,需要模糊查询里面每一项时比如是这样一个数组条件:newstring[]{兴业银行, ... [详细]
  • 本文讨论了微软的STL容器类是否线程安全。根据MSDN的回答,STL容器类包括vector、deque、list、queue、stack、priority_queue、valarray、map、hash_map、multimap、hash_multimap、set、hash_set、multiset、hash_multiset、basic_string和bitset。对于单个对象来说,多个线程同时读取是安全的。但如果一个线程正在写入一个对象,那么所有的读写操作都需要进行同步。 ... [详细]
author-avatar
mobiledu2502859507
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有