Java的集合体系及相关数据结构---Collection系列

​​​​​​​

其中,有序和无序是指存取时候的顺序一致或者不一致; 

Collection是单列集合的顶层接口,它的方法全部单列集合可用。 

Collection方法

方法名说明
boolean add(E e)添加元素
boolean remove(Object o)从集合中移除指定的元素
boolean removeIf(Object o)根据条件进行移除
void clear()清空集合中的元素
boolean contains(Object o)判断集合中是否存在指定的元素
boolean isEmpty()判断集合是否为空
int size()集合的长度,也就是集合中元素的个数

演示:

        Collection<String> collection = new ArrayList<>();//往List添加数据这个方法永远返回true//往set添加可能因为已经存在从而返回falsecollection.add("aaaaaaa");//清除集合全部元素collection.clear();//通过对象删除System.out.println(collection.remove("aaaaaaa"));//判断是否包含元素//要注意,要是集合装的是自定义对象,若想用contains方法,需要重写//alt ins 快速重写System.out.println(collection.contains("aaaaaaa"));//判断是否为空System.out.println(collection.isEmpty());

遍历方式

迭代器遍历

迭代器不依赖索引

注意:迭代器用完指针不复位;且迭代器遍历时候不可用集合的方法增删,只能用迭代器的方法;next()方法其实干了两件事:返回值并移动指针;要配合hasNext和next方法使用

        Collection<String> c = new ArrayList<>();//添加元素c.add("hello");c.add("world");c.add("java");c.add("javaee");Iterator<String> iterator = c.iterator();while (iterator.hasNext()){System.out.println(iterator.next());}

增强for遍历

  • 它是JDK5之后出现的,其内部原理是一个Iterator迭代器
  • 实现Iterable接口的类才可以使用迭代器和增强for
  • 所有单列集合以及数组才能用
  • 集合名字.for快捷生成
        Collection<String> c = new ArrayList<>();//添加元素c.add("hello");c.add("world");c.add("java");c.add("javaee");for(String s :c){System.out.println(s);}

注意:在循环中修改增强for的第三方变量(就比如说上面的s) 不会改变集合值


Lambda表达式遍历 

简化写法的遍历方式,这种方式尤为简洁,底层还是增强for遍历

 public static void main(String[] args) {/* lambda表达式遍历:default void forEach(Consumer<? super T> action):*///1.创建集合并添加元素Collection<String> coll = new ArrayList<>();coll.add("zhangsan");coll.add("lisi");coll.add("wangwu");//2.利用匿名内部类的形式//底层原理://其实也会自己遍历集合,依次得到每一个元素//把得到的每一个元素,传递给下面的accept方法//s依次表示集合中的每一个数据/* coll.forEach(new Consumer<String>() {@Overridepublic void accept(String s) {System.out.println(s);}});*///lambda表达式coll.forEach(s -> System.out.println(s));}

List集合的方法

首先,针对上述方法,由于List是Collection的继承接口,故都能用,但由于它自己有索引,所以多了不少额外的方法:

方法名描述
void add(int index,E element)在此集合中的指定位置插入指定的元素
E remove(int index)删除指定索引处的元素,返回被删除的元素
E set(int index,E element)修改指定索引处的元素,返回被修改的元素
E get(int index)返回指定索引处的元素

 其中,对于add方法,若原位置有元素,则不会覆盖,会往后移动,相当于insert;

而对于remove有两种方式,一种是通过删除索引对应的元素,一种是通过数据值删除元素,当list存储的是Integer对象时,不加说明会按照索引来删除。

 public static void main(String[] args) {List<String> list = new ArrayList<>();list.add("aaa");list.add("bbb");list.add("ccc");//method1(list);//method2(list);//method3(list);//method4(list);}private static void method4(List<String> list) {//        E get(int index)		返回指定索引处的元素String s = list.get(0);System.out.println(s);}private static void method3(List<String> list) {//        E set(int index,E element)	修改指定索引处的元素,返回被修改的元素//被替换的那个元素,在集合中就不存在了.String result = list.set(0, "qqq");System.out.println(result);System.out.println(list);}private static void method2(List<String> list) {//        E remove(int index)		删除指定索引处的元素,返回被删除的元素//在List集合中有两个删除的方法//第一个 删除指定的元素,返回值表示当前元素是否删除成功//第二个 删除指定索引的元素,返回值表示实际删除的元素String s = list.remove(0);System.out.println(s);System.out.println(list);}private static void method1(List<String> list) {//        void add(int index,E element)	在此集合中的指定位置插入指定的元素//原来位置上的元素往后挪一个索引.list.add(0,"qqq");System.out.println(list);}

遍历方式

//创建集合并添加元素
List<String> list = new ArrayList<>();
list.add("aaa");
list.add("bbb");
list.add("ccc");//1.迭代器
/*Iterator<String> it = list.iterator();while(it.hasNext()){String str = it.next();System.out.println(str);
}*///2.增强for
//下面的变量s,其实就是一个第三方的变量而已。
//在循环的过程中,依次表示集合中的每一个元素
/* for (String s : list) {System.out.println(s);}*///3.Lambda表达式
//forEach方法的底层其实就是一个循环遍历,依次得到集合中的每一个元素
//并把每一个元素传递给下面的accept方法
//accept方法的形参s,依次表示集合中的每一个元素
//list.forEach(s->System.out.println(s) );//4.普通for循环
//size方法跟get方法还有循环结合的方式,利用索引获取到集合中的每一个元素
/*for (int i = 0; i < list.size(); i++) {//i:依次表示集合中的每一个索引String s = list.get(i);System.out.println(s);}*/// 5.列表迭代器
//获取一个列表迭代器的对象,里面的指针默认也是指向0索引的//额外添加了一个方法:在遍历的过程中,可以添加元素
ListIterator<String> it = list.listIterator();
while(it.hasNext()){String str = it.next();if("bbb".equals(str)){//qqqit.add("qqq");}
}
System.out.println(list);

不同的是,有一个有一个List专有的列表迭代器ListIterator,这个继承于Iterator,有自己的add和remove方法。

这几种遍历方法都能达到目的,但是想遍历时候修改建议用迭代器或者列表迭代器方式;当然for也可以,只不过得整个索引;单纯遍历用增强for或者lambda(通用);

List的实现类

首先得了解数据结构;

什么是数据结构? -数据结构是计算机存储、组织数据的方式,为了更好的管理和使用数据,结合具体需求来选择;

栈:后进先出,先进后出;

数据ABCD依次进栈,进完后,效果如图所示:

其中,D称为栈顶元素,A称为栈底元素;

出栈则从栈顶开始依次出去;这就是先进后出的含义;

Java中,方法执行时候就是这么干的;

 队列:先进先出,后进后出

同样地,ABCD依次进队,完成后效果如图:

因此出队列方式就从A开始向上依次出去;

数组:查询快,增删慢 

查询快是因为只要有数组地址和索引,就可以快速定位;

增删慢则是因为每次增加和删除,其后位置的数据都要移位,所以慢;

链表:数据独立,查询慢,增删快

链表就和绳子打结类似。每一个节点既存了数据,又存了下一个元素的地址,还有它本身的地址;

创建一个链表:

因此对于链表的增删只需把它前一个记录的地址值或者它自己记录的地址值改掉即可,相对效率就快了;

除此之外,还有一种双向链表,查询效率更高一些:

ArrayList 自动扩容原理

方法这些不说了,详见之前的ArrayList集合;

  1. 创建ArrayList对象的时候,若使用空参构造,则他在底层先创建了一个长度为0的数组,数组名字:elementDate,定义变量size。
  2. 添加第一个元素时候,会创建一个新的长度为10的数组。
  3. 存满后,还要继续,就要扩容,长度为原来的1.5倍。再存满,再扩容1.5倍,循环;
  4. 若一次用addAll方法添加多个元素,超过了15,那就以实际添加数据的长度为准; 

LinkedList集合

这个底层就是双链表。LinkedList是有索引的,我看网上说没有,我还去验证了一下:

        LinkedList<String> strings = new LinkedList<>();strings.add("aaa");strings.add("bbb");strings.add("ccc");strings.add("ddd");for (int i = 0; i < strings.size(); i++) {System.out.print(strings.get(i)); //aaabbbcccddd}

但是呢,这个索引本质上还是用next实现的,不像数组那样可以高效读取;

它本身提供的方法不常用,用父接口的方法就好 


泛型

泛型,以ArrayList举例,那个<>就是装的泛型,用来指明数据类型;早期是没有泛型的。

现在,Java中的泛型是伪泛型,你在写代码时候指明了泛型,但是编译完会进行擦除,即泛型擦除;更多的,泛型在现在是用来标志作用;

  • 泛型只能存引用数据类型;
  • 指明泛型后,仍然可以传递其子类型(不常用);
  • 不写泛型,默认Object;

泛型的定义 

泛型类:

当使用某个类时候,某个变量的数据类型不确定,就可以定义泛型类:

修饰符 class 类名<类型>{}

自定义泛型类示例:

public class MyArrayList<E> {Object[] objects = new Object[10];int size;//这个E相当于是指代前面提到的不确定类型 Epublic boolean add(E e){objects[size] = e;size++;return true;}public E get(int index){return (E)objects[index];}public String toString(){return Arrays.toString(this.objects);}

使用自定义泛型类:

        MyArrayList<String> myArrayList = new MyArrayList<>();myArrayList.add("aaa");myArrayList.add("bbb");myArrayList.add("ccc");System.out.println(myArrayList.toString());
 泛型方法创建
    //不确定的类型E要写在修饰符最后面public static <E> void addAll(ArrayList<E> arrayList,E e1,E e2,E e3,E e4) {arrayList.add(e1);arrayList.add(e2);arrayList.add(e3);arrayList.add(e4);}

调用: 

        ArrayList<String> list =new ArrayList<>();ListUtil.addAll(list,"aaa","bbb","ccc","ddd");System.out.println(list);

 泛型方法有缺陷,即不管你是什么类型,只要是E,方法就会执行,从而产生错误,比如泛型方法里往一个数组添加数据时候。

为了解决这种问题,出现了泛型通配符即类似:

public static void method(ArrayList<? extends A> list){}

 <? extends A>就是泛型通配符,表示这个参数只能为A及其子类;

把extends换成super就表示A及其父类;

泛型接口

这个就在名字后面加<E>就行了;

具体使用方式要么在实现类中给出具体类型,要么在实现类的对象创建时候明确类型

泛型练习:

        /*需求:定义一个继承结构:动物|                           |猫                          狗|      |                    |      |波斯猫   狸花猫                泰迪   哈士奇属性:名字,年龄行为:吃东西波斯猫方法体打印:一只叫做XXX的,X岁的波斯猫,正在吃小饼干狸花猫方法体打印:一只叫做XXX的,X岁的狸花猫,正在吃鱼泰迪方法体打印:一只叫做XXX的,X岁的泰迪,正在吃骨头,边吃边蹭哈士奇方法体打印:一只叫做XXX的,X岁的哈士奇,正在吃骨头,边吃边拆家测试类中定义一个方法用于饲养动物public static void keepPet(ArrayList<???> list){//遍历集合,调用动物的eat方法}要求1:该方法能养所有品种的猫,但是不能养狗要求2:该方法能养所有品种的狗,但是不能养猫要求3:该方法能养所有的动物,但是不能传递其他类型*/

 答案在附件里,没写全,写了一个举例;


Set系列

先了解数据结构。

把子节点放大,你会得到:

  • 每一个节点的子节点数量称为度;
  • 上述结构又可称为二叉树;
  • 树的层数又叫树高;
  • 最上面的节点称为根节点; 
  • 跟18连一块的节点称为左子树;同理,右边的叫右子树;


二叉查找树

添加规则:小的往左,大的往右,一样的不存; 

弊端: 

根节点选的不合适会导致瘸腿,树高太高,查找无疑是不方便的;为了解决这种问题,引入平衡二叉树


平衡二叉树

规则:任意左右子树高度差不超过1

上面这个就不是,10的左右子树长度不一致;

下面这俩就是;

平衡二叉树旋转机制 

左旋:

旋转之后:

稍微复杂的情况:

 ↓

右旋 

  ↓

复杂情况: 

  ↓

四种选择左右旋的情形:

下图这种情况进行一次右旋即可;

 下面这种情况先局部使用左旋,让他变成上图情况,最后使用右旋即可;

 剩下两种情况与上述两种情况刚好相反,不再赘述;

红黑树

红黑树规则 


二叉树遍历方式(二叉树都可以用)

前序遍历

中序遍历 

后序遍历

*20在最后,图没截全 

层序遍历

Set系列集合

特点:

  • 不可以存储重复元素没有索引
  • 不能使用普通for循环遍历
  • 可以使用Collection方法

add方法的返回值要关注,因为不能有重复值,不一定添加成功; 

        Set<String> set = new TreeSet<>();//添加元素set.add("ccc");set.add("aaa");set.add("aaa");set.add("bbb");//遍历集合Iterator<String> it = set.iterator();while (it.hasNext()){String s = it.next();System.out.println(s);}System.out.println("-----------------------------------");for (String s : set) {System.out.println(s);}

HashSet

Hash值:对象的整数表现形式

  • 定义在Object类中,所有对象可用,默认根据地址值计算;
  • 一般需要重写hashCode方法
  • 部分情况下,不同地址值计算出来的Hash值有可能相同,称为Hash碰撞,概率较小;
  • 存储自定义对象一定要重写hashcode和equals方法
  • LinkedHashSet有序; 
  • 有数据去重需求时候使用HashSet;

TreeSet 

不必学习它的额外方法,Collection提供的就够了;因为它是基于红黑树的,所以他的存取效率都比较高,而且默认排序,0的时候就代表认为这两个对象相同;

        //创建集合对象TreeSet<Integer> ts = new TreeSet<Integer>();//添加元素ts.add(10);ts.add(40);ts.add(30);ts.add(50);ts.add(20);ts.add(30);//遍历集合for(Integer i : ts) {System.out.println(i);
  •  对于数值类型,比如Integer,Double等,按照大小排序;
  • 对于String,Char按照字符在ASCII表中的数字升序排序;若字符串较长,先比第一个字母,第一个相同比第二个,依次往后推

对于自定义对象,排序方法有两种,一种是实现Comparable接口,称为默认排序或者自然排序;重写的方法和之前那个快速排序法有点类似: 

public class Student implements Comparable<Student>{private String name;private int age;public Student() {}public Student(String name, int age) {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;}@Overridepublic String toString() {return "Student{" +"name='" + name + '\'' +", age=" + age +'}';}@Overridepublic int compareTo(Student o) {//按照对象的年龄进行排序//主要判断条件: 按照年龄从小到大排序int result = this.age - o.age;//次要判断条件: 年龄相同时,按照姓名的字母顺序排序result = result == 0 ? this.name.compareTo(o.getName()) : result;return result;}
}
public static void main(String[] args) {//创建集合对象TreeSet<Student> ts = new TreeSet<>();//创建学生对象Student s1 = new Student("zhangsan",28);Student s2 = new Student("lisi",27);Student s3 = new Student("wangwu",29);Student s4 = new Student("zhaoliu",28);Student s5 = new Student("qianqi",30);//把学生添加到集合ts.add(s1);ts.add(s2);ts.add(s3);ts.add(s4);ts.add(s5);//遍历集合for (Student student : ts) {System.out.println(student);}}

 第二种方式是比较器排序

这种适用于Java中已经写好的类比如String,它自己重写的CompareTo方法不满足需求了,就要用到Comparator对象;

这种就是匿名内部类的实现方式,可以改写成lambda表达式:

	//创建集合对象TreeSet<Teacher> ts = new TreeSet<>(new Comparator<Teacher>() {@Overridepublic int compare(Teacher o1, Teacher o2) {//o1表示现在要存入的那个元素//o2表示已经存入到集合中的元素//主要条件int result = o1.getAge() - o2.getAge();//次要条件result = result == 0 ? o1.getName().compareTo(o2.getName()) : result;return result;}});//创建老师对象Teacher t1 = new Teacher("zhangsan",23);Teacher t2 = new Teacher("lisi",22);Teacher t3 = new Teacher("wangwu",24);Teacher t4 = new Teacher("zhaoliu",24);//把老师添加到集合ts.add(t1);ts.add(t2);ts.add(t3);ts.add(t4);//遍历集合for (Teacher teacher : ts) {System.out.println(teacher);}

单列集合的选择:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/759661.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

语音转文字——sherpa ncnn语音识别离线部署C++实现

简介 Sherpa是一个中文语音识别的项目&#xff0c;使用了PyTorch 进行语音识别模型的训练&#xff0c;然后训练好的模型导出成 torchscript 格式&#xff0c;以便在 C 环境中进行推理。尽管 PyTorch 在 CPU 和 GPU 上有良好的支持&#xff0c;但它可能对资源的要求较高&#x…

HarmonyOS 应用开发案例

本帖下方集中了HarmonyOS Next应用开发时&#xff0c;会遇到的常见应用案例。后续会持续更新大量案例&#xff0c;帮助开发者快速学习。欢迎感兴趣的同学加入Q&#xff1a;454901491 72.手写绘制及保存图片案例&#xff08;0319更新&#xff09;&#xff08;点此查看源码实现&…

Windows10无盘母盘制作-以云更新为例

Windows10无盘母盘制作-以云更新为例 缘起环境准备创建虚拟机安装系统导出系统 缘起 网吧客户端在实际环境中&#xff0c;经常要面对形形色色对无盘系统&#xff0c;五花八门对无盘镜像&#xff0c; 为了方便确认不同无盘环境对客户的对影响&#xff0c;决定自己制作一个无盘母…

【python + Django】Django模板语法 + 请求和响应

前言&#xff1a; 现在现在&#xff0c;我们要开始将变量的值展现在页面上面啦&#xff01; 要是只会显示静态页面&#xff0c;我们的页面也太难看和死板了&#xff0c; 并且数据库的数据也没法展现在页面上。 但是呢&#xff0c;模板语法学习之后就可以啦&#xff01;&…

【大模型服务】01EdgeFM: Leveraging Foundation Model for Open-set Learning on the Edge

该文发表在 SenSys’23(CCF B) 上&#xff0c;作者是来自港中文的鄢振宇。这是一篇关于云端协同的文章&#xff0c;主要解决边缘设备深度模型的泛化性不足问题&#xff0c;实现 Open-set Learning。 文章目录 背景Open-Set Recognition 的挑战EdgeFM 整体架构图云端&#xff1…

21---EEPROM电路设计

视频链接 EEPROM电路设计01_哔哩哔哩_bilibili EEPROM电路设计 1、存储器的分类 一般根据掉电丢失来划分的存储器。可分为易失性存储器和非易失性储存器。 ROM在系统停止供电的时候仍然可以保持数据&#xff0c;而RAM通常都是在掉电之后就丢失数据。 1.1、易失性存储器-R…

升级 HarmonyOS 4 版本,腕上智慧更进一步

HUAWEI WATCH GT 3 系列升级 HarmonyOS 4 新版本后&#xff0c;手表体验更进一步&#xff0c;快来看看有哪些变化吧~

如何进行端口映射端口转发?

在互联网时代&#xff0c;网络安全成为一项重要的任务。端口映射端口转发是一种常用的网络安全技术&#xff0c;它可以实现远程访问内部网络资源的需求。本文将介绍端口映射端口转发的基本原理和应用场景&#xff0c;以及一种名为"天联"的解决方案。 基本原理 端口映…

种植新革命:科技赋能绿色未来

《种植新革命&#xff1a;科技赋能绿色未来》 一、种植技术的颠覆式创新 随着科技的飞速发展&#xff0c;种植技术也在经历一场颠覆式的创新。传统的种植方式&#xff0c;虽然历史悠久&#xff0c;经验丰富&#xff0c;但在面对现代化、大规模、高效的需求时&#xff0c;逐渐…

LeetCode每日一题——最后一个单词的长度

最后一个单词的长度OJ链接&#xff1a;58. 最后一个单词的长度 - 力扣&#xff08;LeetCode&#xff09; 题目&#xff1a; 思路 &#xff1a; 统计字符串中最后一个单词的长度&#xff0c;那么我们可以定一一个指针&#xff0c;从后向前开始统计&#xff0c;当指针指向的元素…

【LabVIEW FPGA入门】局部变量和全局变量

局部变量 无法访问某前面板对象或需要在程序框图节点之间传递数据时&#xff0c;可创建前面板对象的局部变量。创建局部变量后&#xff0c;局部变量仅仅出现在程序框图上&#xff0c;而不在前面板上。 局部变量可对前面板上的输入控件或显示件进行数据读写。写入局部变量相当于…

流畅的 Python 第二版(GPT 重译)(十三)

第二十四章&#xff1a;类元编程 每个人都知道调试比一开始编写程序要困难两倍。所以如果你在编写时尽可能聪明&#xff0c;那么你将如何调试呢&#xff1f; Brian W. Kernighan 和 P. J. Plauger&#xff0c;《编程风格的要素》 类元编程是在运行时创建或自定义类的艺术。在 P…

局域网内怎么控制另一台电脑

局域网内怎么控制另一台电脑 在局域网内控制另一台电脑是一个相对高级的操作&#xff0c;通常用于远程管理、技术支持或资源共享等场景。要实现这一功能&#xff0c;需要确保两台电脑都连接到同一个局域网&#xff0c;并且已进行适当的配置。以下是几种常见的方法&#xff1a;…

【蓝桥杯选拔赛真题42】C++切蛋糕 第十四届蓝桥杯青少年创意编程大赛 算法思维 C++编程选拔赛真题解析

目录 C切蛋糕 一、题目要求 1、编程实现 2、输入输出 二、算法分析 三、程序编写 四、程序说明 五、运行结果 六、考点分析 七、推荐资料 C切蛋糕 第十四届蓝桥杯青少年创意编程大赛C选拔赛真题 一、题目要求 1、编程实现 有一块矩形蛋糕&#xff0c;小明从蛋糕…

GCN-Align论文翻译

GCN-Align翻译 Cross-lingual Knowledge Graph Alignment via Graph Convolutional Networks 基于图卷积网络的跨语言知识图谱对齐 Abstract 多语言知识图&#xff08;KGs&#xff09;&#xff0c;如DBpedia和YAGO&#xff0c;包含几种不同语言的实体结构化知识&#xff0c…

墨菲安全在软件供应链安全领域阶段性总结及思考

向外看&#xff1a;墨菲安全在软件供应链安全领域的一些洞察、思考、行动 洞察 现状&挑战&#xff1a; 过去开发安全体系是无法解决软件供应链安全问题的&#xff1b;一些过去专注开发安全领域的厂商正在错误的引导行业用开发安全思维解决软件供应链安全问题&#xff0c;治…

SpringCloud中的@EnableDiscoceryClient和@EnableFeignClients注解的作用解析、RPC远程过程调用

目录 EnableDiscoveryClient 服务发现的核心概念 服务注册中心 EnableDiscoveryClient注解的作用 服务心跳健康检查 使用示例 EnableFeignClients Feign简介 EnableFeignClients注解的作用 RPC&#xff08;Remote Procedure Call&#xff09; 参考链接 Spring Cloud…

javaScript——BFS结合队列求迷宫最短路径

这里推荐先去看下B站这个老师讲的BFS迷宫问题&#xff0c;只用看前五分钟就能懂用BFS队列实现的原理。[POJ] 3984 迷宫问题 BFS_哔哩哔哩_bilibili 问题描述&#xff1a;由m*n的矩阵构成了一个迷宫&#xff0c; 矩阵中为1的元素表示障碍物&#xff0c;不能走&#xff0c;为0表示…

如何在职场中提升自己的竞争力

随着社会的发展&#xff0c;职场竞争日益激烈。如何在职场中提升自己的竞争力&#xff0c;成为许多职场人士关注的焦点。本文将从以下几个方面为大家提供一些建议。 一、不断学习&#xff0c;提升专业技能 在职场中&#xff0c;专业技能是衡量一个人竞争力的重要标准。要想在职…

让数据在两个buckets之间传输 - Google Storage Transfer Service

在业务场景中&#xff0c; 有时我们不想直接暴露数据存储空间给上游系统&#xff0c; 而需要设置1个landing Path 让上游系统发送数据 如图&#xff1a; 我们只需grant landing bucket 的权限给上游系统&#xff0c; 而上游系统是访问不了storage bucket的保证了数据隔离 但是…