JavaSE基础笔记—集合之Collection
集合
数组的优缺点
1、集合、数组都是对多个数据进行存储操作的结构,简称Java容器。
说明:此时的存储,主要指的是内存层面的存储,不涉及到持久化的存储(.txt、.jpg、.avi、数据库)。
2.1、数组在存储多个数据方面的特点
1)一旦初始化定义好以后,其长度就确定了;
2)数组一旦定义好,其元素类型也就确定了,我们也只能操作指定类型的数据。比如:String [ ] arr、int [ ] arr2、Object [ ] arr3;
2.2、数组在存储多个数据方面的缺点
1)一旦初始化定义好以后,其长度就不可以修改了;
2)数组中提供的方法非常有限,对于添加、删除、插入数据等操作非常不便,效率也不高;
3)获取数组中实际元素个数的需求,数组没有提供现成的属性或方法可用
4)数组存储数据的特点:有序、可重复;对于无序、不可重复的需求不能满足
Java集合可分为两种体系
Collection接口继承树
Map接口继承树
Collection接口
单列集合,用来存储一个一个的对象
Map接口:双列集合,用来存储一对一对的数据(key—value)——高中讲的函数(y=f(x))
Map接口下的主要实现类:HashMap、LinkedHashMap、TreeMap、HashTable、Properties
Collection接口中的常用方法
package com.harrison.java2;import org.junit.Test;import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;/*** @author Harrison* @create 2022-06-06-12:41* @motto 众里寻他千百度,蓦然回首,那人却在灯火阑珊处。*/
public class CollectionTest {@Testpublic void test1(){Collection coll = new ArrayList();//add(Object e):将元素e添加到集合coll中coll.add("AA");coll.add("BB");coll.add(123);//自动装箱coll.add(new Date());//size():获取添加的元素的个数System.out.println(coll.size());//4//addAll(Collection coll1):将coll1集合中的元素添加到当前的集合中Collection coll1 = new ArrayList();coll1.add(456);coll1.add("CC");coll.addAll(coll1);System.out.println(coll.size());//6System.out.println(coll);//clear():清空集合元素coll.clear();//isEmpty():判断当前集合是否为空System.out.println(coll.isEmpty());}
}
下面的Person类是自己写的!
@Testpublic void test2(){Collection coll = new ArrayList();coll.add(123);coll.add(456);
// Person p = new Person("Jerry",20);
// coll.add(p);coll.add(new Person("Jerry",20));coll.add(new String("Tom"));coll.add(false);//1.contains(Object obj):判断当前集合中是否包含obj//我们在判断时会调用obj对象所在类的equals()。boolean contains = coll.contains(123);System.out.println(contains);System.out.println(coll.contains(new String("Tom")));
// System.out.println(coll.contains(p));//trueSystem.out.println(coll.contains(new Person("Jerry",20)));//false -->true//2.containsAll(Collection coll1):判断形参coll1中的所有元素是否都存在于当前集合中。Collection coll1 = Arrays.asList(123,4567);System.out.println(coll.containsAll(coll1));}
@Testpublic void test3(){//3.remove(Object obj):从当前集合中移除obj元素。Collection coll = new ArrayList();coll.add(123);coll.add(456);coll.add(new Person("Jerry",20));coll.add(new String("Tom"));coll.add(false);coll.remove(1234);System.out.println(coll);coll.remove(new Person("Jerry",20));System.out.println(coll);//4. removeAll(Collection coll1):差集:从当前集合中移除coll1中所有的元素。Collection coll1 = Arrays.asList(123,456);coll.removeAll(coll1);System.out.println(coll);}
@Testpublic void test4(){Collection coll = new ArrayList();coll.add(123);coll.add(456);coll.add(new Person("Jerry",20));coll.add(new String("Tom"));coll.add(false);//5.retainAll(Collection coll1):交集:获取当前集合和coll1集合的交集,并返回给当前集合
// Collection coll1 = Arrays.asList(123,456,789);
// coll.retainAll(coll1);
// System.out.println(coll);//6.equals(Object obj):要想返回true,需要当前集合和形参集合的元素都相同。Collection coll1 = new ArrayList();coll1.add(456);coll1.add(123);coll1.add(new Person("Jerry",20));coll1.add(new String("Tom"));coll1.add(false);System.out.println(coll.equals(coll1));// false}
@Testpublic void test5(){Collection coll = new ArrayList();coll.add(123);coll.add(456);coll.add(new Person("Jerry",20));coll.add(new String("Tom"));coll.add(false);//7.hashCode():返回当前对象的哈希值System.out.println(coll.hashCode());//8.集合 --->数组:toArray()Object[] arr = coll.toArray();for(int i = 0;i < arr.length;i++){System.out.println(arr[i]);}//拓展:数组 --->集合:调用Arrays类的静态方法asList()List<String> list = Arrays.asList(new String[]{"AA", "BB", "CC"});System.out.println(list);List arr1 = Arrays.asList(new int[]{123, 456});System.out.println(arr1.size());//1List arr2 = Arrays.asList(new Integer[]{123, 456});System.out.println(arr2.size());//2//9.iterator():返回Iterator接口的实例,用于遍历集合元素。放在IteratorTest.java中测试}
结论:向Collection接口的实现类的对象中添加数据obj时,要求obj所在类要重写equals()
使用Iterator遍历Collection
- 集合元素的遍历操作,使用迭代器Iterator接口
- 内部的方法:hasNext() 和 next()
- 集合对象每次调用iterator()方法都得到一个全新的迭代器对象,
- 默认游标都在集合的第一个元素之前。
- 内部定义了remove(),可以在遍历的时候,删除集合中的元素。此方法不同于集合直接调用remove()
- 如果还未调用next()或在上一次调用 next 方法之后已经调用了 remove 方法,再调用remove都会报IllegalStateException。
@Testpublic void test1(){Collection coll = new ArrayList();coll.add(123);coll.add(456);coll.add(new Person("Jerry",20));coll.add(new String("Tom"));coll.add(false);Iterator iterator = coll.iterator();//方式一:
// System.out.println(iterator.next());
// System.out.println(iterator.next());
// System.out.println(iterator.next());
// System.out.println(iterator.next());
// System.out.println(iterator.next());
// //报异常:NoSuchElementException
// System.out.println(iterator.next());//方式二:不推荐
// for(int i = 0;i < coll.size();i++){// System.out.println(iterator.next());
// }//方式三:推荐hasNext():判断是否还有下一个元素while(iterator.hasNext()){//next():①指针下移 ②将下移以后集合位置上的元素返回System.out.println(iterator.next());}}
Iterator遍历集合的两种错误写法
@Testpublic void test2(){Collection coll = new ArrayList();coll.add(123);coll.add(456);coll.add(new Person("Jerry",20));coll.add(new String("Tom"));coll.add(false);//错误方式一:
// Iterator iterator = coll.iterator();
// while((iterator.next()) != null){// System.out.println(iterator.next());
// }//错误方式二://集合对象每次调用iterator()方法都得到一个全新的迭代器对象,默认游标都在集合的第一个元素之前。while (coll.iterator().hasNext()){System.out.println(coll.iterator().next());}}
Iterator迭代器remove方法的使用
//测试Iterator中的remove()//如果还未调用next()或在上一次调用 next 方法之后已经调用了 remove 方法,// 再调用remove都会报IllegalStateException。@Testpublic void test3(){Collection coll = new ArrayList();coll.add(123);coll.add(456);coll.add(new Person("Jerry",20));coll.add(new String("Tom"));coll.add(false);//删除集合中"Tom"Iterator iterator = coll.iterator();while (iterator.hasNext()){// iterator.remove();Object obj = iterator.next();if("Tom".equals(obj)){iterator.remove();
// iterator.remove();}}//遍历集合iterator = coll.iterator();while (iterator.hasNext()){System.out.println(iterator.next());}}
新特性foreach循环遍历集合或项目
package com.harrison.java2;import org.junit.Test;import java.util.ArrayList;
import java.util.Collection;/*** @author Harrison* @create 2022-06-06-15:26* @motto 众里寻他千百度,蓦然回首,那人却在灯火阑珊处。*/
public class ForTest {@Testpublic void test1(){Collection coll = new ArrayList();coll.add(123);coll.add(456);coll.add(new Person("Jerry",20));coll.add(new String("Tom"));coll.add(false);//for(集合元素的类型 局部变量 : 集合对象)//内部仍然调用了迭代器。for(Object obj : coll){System.out.println(obj);}}@Testpublic void test2(){int[] arr = new int[]{1,2,3,4,5,6};//for(数组元素的类型 局部变量 : 数组对象)for(int i : arr){System.out.println(i);}}//练习题@Testpublic void test3(){String[] arr = new String[]{"MM","MM","MM"};// //方式一:普通for赋值
// for(int i = 0;i < arr.length;i++){// arr[i] = "GG";
// }//方式二:增强for循环for(String s : arr){s = "GG";}for(int i = 0;i < arr.length;i++){System.out.println(arr[i]);}}
}
List接口
存储有序的、可重复的数据——“动态”数组。
List接口下的三个主要实现类:ArrayList、LinkedList、Vector
List接口常用实现类的对比
List接口概述
鉴于Java中数组用来存储数据的局限性,我们通常使用List替代数组。List集合类中元素有序、且可重复,集合中的每个元素都有其对应的顺序索引。List容器中的元素都对应一个整数型的序号记载其在容器中的位置,可以根据序号存取容器中的元素。JDK API中List接口的实现类常用的有: ArrayList、 LinkedList和Vector.
ArrayList、LinkedList、Vector三者的异同?
同:三个类都是实现了List接口,存储数据的特点相同:存储有序的、可重复的数据
不同
- ArrayList:作为List接口的主要实现类;线程不安全的,效率高;底层使用Object[] elementData存储
- LinkedList:对于频繁的插入、删除操作,使用此类效率比ArrayList高;底层使用双向链表存储
- Vector:作为List接口的古老实现类;线程安全的,效率低;底层使用Object[] elementData存储
ArrayList源码分析
jdk 7情况下
ArrayList list = new ArrayList();//底层创建了长度是10的Object[]数组elementData
list.add(123);//elementData[0] = new Integer(123);
…
list.add(11);//如果此次的添加导致底层elementData数组容量不够,则扩容。
默认情况下,扩容为原来的容量的1.5倍,同时需要将原有数组中的数据复制到新的数组中。
结论:建议开发中使用带参的构造器:ArrayList list = new ArrayList(int capacity)
jdk 8中ArrayList的变化
ArrayList list = new ArrayList();//底层Object[] elementData初始化为{}.并没有创建长度为10的数组
list.add(123);//第一次调用add()时,底层才创建了长度10的数组,并将数据123添加到elementData[0]
…
后续的添加和扩容操作与jdk 7 无异。
小结
jdk7中的ArrayList的对象的创建类似于单例的饿汉式,而jdk8中的ArrayList的对象的创建类似于单例的懒汉式,延迟了数组的创建,节省内存。
LinkedList源码分析
jdk 8情况下
LinkedList list = new LinkedList(); 内部声明了Node类型的first和last属性,默认值为null
list.add(123);//将123封装到Node中,创建了Node对象。
其中,Node定义为:体现了LinkedList的双向链表的说法
private static class Node<E> {E item;Node<E> next;Node<E> prev;Node(Node<E> prev, E element, Node<E> next) {this.item = element;this.next = next;this.prev = prev;}}
Vector源码分析
jdk7和jdk8中通过Vector()构造器创建对象时,底层都创建了长度为10的数组。在扩容方面,默认扩容为原来的数组长度的2倍
List接口中的常用方法测试
Listf除了从Collection集合继承的方法外,List 集合里添加了一些根据索引来操作集合元素的方法。
- void add(int index, Object ele):在index位置插入ele元素
- boolean addAll(int index, Collection eles):从index位置开始将eles中的所有元素添加进来
- Object get(int index):获取指定index位置的元素
- int indexOf(Object obj):返回obj在集合中首次出现的位置
- int lastIndexOf(Object obj):返回obj在当前集合中末次出现的位置
- Object remove(int index):移除指定index位置的元素,并返回此元素
- Object set(int index, Object ele):设置指定index位置的元素为ele
- List subList(int fromIndex, int toIndex):返回从fromIndex到toIndex位置的子集合
List遍历及方法总结
总结(常用方法)
- 增:add(Object obj)
- 删:remove(int index) / remove(Object obj)
- 改:set(int index, Object ele)
- 查:get(int index)
- 插:add(int index, Object ele)
- 长度:size()
- 遍历:① Iterator迭代器方式 ② 增强for循环 ③ 普通的循环
@Testpublic void test3(){ArrayList list = new ArrayList();list.add(123);list.add(456);list.add("AA");//方式一:Iterator迭代器方式Iterator iterator = list.iterator();while(iterator.hasNext()){System.out.println(iterator.next());}System.out.println("***************");//方式二:增强for循环for(Object obj : list){System.out.println(obj);}System.out.println("***************");//方式三:普通for循环for(int i = 0;i < list.size();i++){System.out.println(list.get(i));}}
List的一个小面试题
public class ListTest {/*区分List中remove(int index)和remove(Object obj)*/@Testpublic void testListRemove() {List list = new ArrayList();list.add(1);list.add(2);list.add(3);updateList(list);System.out.println(list);//}private void updateList(List list) {// list.remove(2);list.remove(new Integer(2));}
}
Set接口
存储无序的、不可重复的数据——高中讲的“集合”
Set接口下的三个主要实现类:HashSet、LinkedHashSet、TreeSet
概述:Set接口是Collection的子接口,set接口没有提供额外的方法;Set集合不允许包含相同的元素,如果试把两个相同的元素加入同一个Set集合中,则添加操作失败。Set判断两个对象是否相同不是使用==运算符,而是根据equals()方法
Set接口实现类的对比
- HashSet:作为Set接口的主要实现类;线程不安全的;可以存储null
- LinkedHashSet:作为HashSet的子类;遍历其内部数据时,可以按照添加的顺序遍历
- 对于频繁的遍历操作,LinkedHashSet效率高于HashSet.
- TreeSet:可以按照添加对象的指定属性,进行排序。
- Set接口中没有额外定义新的方法,使用的都是Collection中声明过的方法。
Set的无序性与不可重复性的理解
以HashSet为例说明
1. 无序性:不等于随机性。存储的数据在底层数组中并非按照数组索引的顺序添加,而是根据数据的哈希值决定的。2. 不可重复性:保证添加的元素按照equals()判断时,不能返回true.即:相同的元素只能添加一个。
HashSet的使用
HashSet中元素的添加过程
HashSet底层也是数组,初始容量为16,当如果使用率超过0.75,(16*0.75=12);就会扩大容量为原来的2倍。(16扩 容为32,依次为64,12…等)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VbLyFR62-1654564144136)(D:/Study/blogImg/image-20220607003834236.png)]
我们向HashSet中添加元素a,首先调用元素a所在类的hashCode()方法,计算元素a的哈希值,此哈希值接着通过某种算法计算出在HashSet底层数组中的存放位置(即为:索引位置),判断数组此位置上是否已经有元素:
如果此位置上没有其他元素,则元素a添加成功。 —>情况1
如果此位置上有其他元素b(或以链表形式存在的多个元素),则比较元素a与元素b的hash值:
如果hash值不相同,则元素a添加成功。—>情况2
如果hash值相同,进而需要调用元素a所在类的equals()方法:
equals()返回true,元素a添加失败
equals()返回false,则元素a添加成功。—>情况2
对于添加成功的情况2和情况3而言:元素a 与已经存在指定索引位置上数据以链表的方式存储。
jdk 7 :元素a放到数组中,指向原来的元素。
jdk 8 :原来的元素在数组中,指向元素a
总结:七上八下
HashSet底层:数组+链表的结构。
HashCode和equals()的重写
重写hashCode()方法的基本原则
- 在程序运行时,同一个对象多次调用hashCode()方法应该返回相同的值。
- 当两个对象的equals()方法比较返回true时,这两个对象的hashCode()方法的返回值也应相等。
- 对象中用作equals()方法比较的Field,都应该用来计算hashCode值。
向Set(主要指:HashSet、LinkedHashSet)中添加的数据,其所在的类一定要重写hashCode()和equals()
要求:重写的hashCode()和equals()尽可能保持一致性:相等的对象必须具有相等的散列码
重写两个方法的小技巧:对象中用作 equals() 方法比较的 Field,都应该用来计算 hashCode 值。
LinkedHashSet的使用
LinkedHashSet作为HashSet的子类,在添加数据的同时,每个数据还维护了两个引用,记录此数据前一个数据和后一个数据。
优点:对于频繁的遍历操作,LinkedHashSet效率高于HashSet
TreeSet的使用
TreeSet的自然排序和定制排序
- 向TreeSet中添加的数据,要求是相同类的对象。
- 两种排序方式:自然排序(实现Comparable接口) 和 定制排序(Comparator)
- 自然排序中,比较两个对象是否相同的标准为:compareTo()返回0.不再是equals().
- 定制排序中,比较两个对象是否相同的标准为:compare()返回0.不再是equals()
e值。
向Set(主要指:HashSet、LinkedHashSet)中添加的数据,其所在的类一定要重写hashCode()和equals()
要求:重写的hashCode()和equals()尽可能保持一致性:相等的对象必须具有相等的散列码
重写两个方法的小技巧:对象中用作 equals() 方法比较的 Field,都应该用来计算 hashCode 值。
LinkedHashSet的使用
LinkedHashSet作为HashSet的子类,在添加数据的同时,每个数据还维护了两个引用,记录此数据前一个数据和后一个数据。
优点:对于频繁的遍历操作,LinkedHashSet效率高于HashSet
TreeSet的使用
TreeSet的自然排序和定制排序
- 向TreeSet中添加的数据,要求是相同类的对象。
- 两种排序方式:自然排序(实现Comparable接口) 和 定制排序(Comparator)
- 自然排序中,比较两个对象是否相同的标准为:compareTo()返回0.不再是equals().
- 定制排序中,比较两个对象是否相同的标准为:compare()返回0.不再是equals()
JavaSE基础笔记—集合之Collection相关推荐
- JavaSE基础笔记——常用API、Lambda、常见算法
日期与时间 时间日期是在任何一个程序系统里几乎都不可能忽略掉的数据量,而且大量的算法在底层都会使用到时间日期数据值作为算法的基本种子(随机数算法或加密算法都经常用到). 计算机里,时间日期的本质 作为 ...
- JavaScript基础笔记集合(转)
JavaScript基础笔记集合 JavaScript基础笔记集合 js简介 js是脚本语言.浏览器是逐行的读取代码,而传统编程会在执行前进行编译 js存放的位置 html脚本必须放在< ...
- -1-3 java集合框架基础 java集合体系结构 Collection 常用java集合框架 如何选择集合 迭代器 泛型 通配符概念 Properties 集合 迭代器...
集合又称之为容器存储对象的一种方式 •数组虽然也可以存储对象,但长度是固定的:显然需要可变长度的容器 集合和数组的区别? A:长度区别 ...
- JavaSE基础学习——集合
1. 集合的概述 集合是Java中存储对象的一种容器. 集合的大小不固定,启动后可以动态变化,类型也可以选择不固定.集合更像气球, 集合非常适合做元素的增删操作. 注意:集合中只能存储引用类型数据,如 ...
- Java基础(集合框架——Collection、List、Set、泛型)
为什么出现集合类? 面向对象语言对事物的体现都是以对象的形式,所以为了方便对多 个对象的操作,就对对象进行存储,集合就是存储对象常用的一 种方式. 数组和集合类同是容器,有何不同? 数组虽然也可以存储 ...
- JavaSE基础笔记——不可变集合简介、Stream流体系、异常处理
1.不可变集合简介 不可变集合,就是不可被修改的集合. 集合的数据项在创建的时候提供,并且在整个生命周期中都不可改变.否则报错. 为什么要创建不可变集合? 如果某个数据不能被修改,把它防御性地拷贝到不 ...
- JavaSE基础笔记(全)
文章目录 JAVA语言的初识 JDK的下载及安装 简单的DOS命令 1.打开cmd: 2.常用的Dos命令 JDK.JRE.JVM JDK JRE JVM JDK.JRE.JVM三者之间的关系 JDK ...
- JavaSE基础笔记——Javaoo(面向对象进阶:static与继承)
1.Static: static是什么? 它是静态的意思,可以修饰成员变量和成员方法. static修饰成员变量表示该成员变量只在内存中只存储一份,可以被共享访问.修改. static修饰成员变量的用 ...
- JavaSE基础笔记八
第六章 异常处理 1.体系结构 java.lang.Object |----java.lang.Throwable |----java.lang.Error:错误,java程序对此无能为力,不显式的处 ...
最新文章
- javascript-对混合字母/数字数组进行排序
- 34.在排序数组中查找元素的起始和末尾位置
- IT项目开发的75条管理守则(转)
- python123子字符串输出_Python--字符串函数方法全解
- OpenCV用thrust使用cv :: cuda :: GpuMat
- MPP 二、Greenplum数据加载
- php oracle 锁表,ORACLE 用户锁定问题
- HDU4405 期望
- python的环境变量设置
- java阅读题_java 练习题
- 路由器网络性能测试软件,路由器性能测试
- alpine(Go环境)Docker基础镜像制作
- WebService实现文件上传下载
- .NET 2.0中的企业库异常处理块
- Java分布式架构:应用+特点+架构模式
- Win10没有Realtek高清晰音频管理器怎么办?
- Linux文件与目录常用操作
- 世界观和方法论----写给学弟学妹的
- 期刊论文发表的作者顺序有什么讲究吗
- 如何让邮件变的更安全?邮箱安全中心在哪里?