C# 有关List<T>的Contains与Equals方法
【以下内容仅为本人在学习中的所感所想,本人水平有限目前尚处学习阶段,如有错误及不妥之处还请各位大佬指正,请谅解,谢谢!】
!!!观前提醒!!!
【本文内容可能较为复杂,虽然我已经以较为清晰的方式展现我的思想,但可能依旧容易引起思维混乱,若感到混乱或不舒服的情况,可直接转跳至文末的总结处;也可以先看完结论再来阅读文章】
引言
List作为一种动态存储结构,可以代替数组,还可以将其当作链表使用。本文将介绍C#中List的相关内容,重点介绍其包含的Contains与Equals方法,并针对集合的比较与去重进行分析,提供可行的解决方案。
起因
题目链接:6049. 含最多 K 个可整除元素的子数组 - 力扣(LeetCode) (leetcode-cn.com)
实际上就是在一个数组的所有非空“子数组”中,统计其中满足要求的子数组的数量。
经过与结果
想法:枚举每一个子数组,判断其是否符合要求;若符合则判断该子数组是否应经被统计过;未统计则加入,已统计则跳过。
结果:(我真的是匪夷所思啊!!!)
【注:将HashSet换为List运行结果相同】 当换用字符串为元素后,结果却没有问题且AC通过
第一部分 关于List
【注:此部分属于介绍内容,非本文重点,可转跳至第二部分】
在分析上面问题之前,我们先对List有一个认知。List是一个C#中最常见的可伸缩数组组件,即动态数据存储结构。我们常常用它来替代数组,并且因为它的长度是动态的,所以我们在写的时候不用手动去分配的大小;甚至有时我们也会拿它当链表使用。
属性
(一)Count
定义:获取 List<T>中包含的元素数。
(二)Capacity
(1)定义:获取或设置该内部数据结构在不调整大小的情况下能够容纳的元素总数。
(2)可能触发的异常:
ArgumentOutOfRangeException:Capacity 被设置为一个小于现有长度的值。
OutOfMemoryException:系统上没有足够的可用内存。
小结
(1)Capacity是可能需要调整大小之前存储的元素数目,以4为基数,每次递增4;Count是实际位于其中List<T>元素的数目。
(2)Capacity始终大于等于Count。如果在添加元素时Count超过Capacity,则会在添加元素前自动重新分配Capacity的值;在清空List<T>或删除元素后Capacity的值不会变化。
(3)可通过TrimExcess()方法删除多余的预留空间,即使Capacity的值等于Count的值。
(3)获取此属性的值的运算时间复杂度为 O(1)。
(三)Item[index]
(1)定义:获取或设置指定索引处的元素。
(2)可能触发的异常:
ArgumentOutOfRangeException:index小于0或大于等于 Count。
常用方法
(一)添加
(1)Add(T) 将对象添加到末尾
(2)AddRange(List<T>) 将集合添加到末尾【注:List<T>非空】
(3)Insert(int index, T) 将元素插入指定索引处
(4)InsertRange(int index, List<T>) 将集合插入指定索引处【注:List<T>非空】
(二)删除
(1)Remove(T) 移除指定对象的第一个匹配项
(2)RemoveAll(Predicate<T> match) 移除与match相匹配的所有元素
(3)RemoveAt(int num) 移除指定s索引处的元素
(4)RemoveRange(int index, int count) 移除指定范围内的元素
(三)查找
(1)Find(Predicate<T> match) 搜索并返回第一个与match相匹配的元素
(2)FindIndex(Predicate<T> match) 返回第一个与match相匹配的元素的索引值
(3)FindIndex(int startIndex, Predicate<T> match) 从startIndex开始搜索,返回第一个与match相匹配的元素的索引值
(4)FindIndex(int startIndex, int count, Predicate<T> match) 从startIndex开始搜索count位,返回第一个与match相匹配的元素的索引值
【注:FindLast、FindLastIndex从后往前搜索,其余部分相同】
(5)IndexOf(T) 搜索指定对象,返回第一个匹配项从零开始的索引
(6)Find(T, int index) 在[index, count)范围内搜索指定对象,返回第一个匹配项从零开始的索引
(7)Find(T, int index, int count) 在[index, index + count]范围内搜索指定对象,返回第一个匹配项从零开始的索引
(四)排序
(1)Sort() 使用默认比较器(升序)对List进行排序
(2)Sort(IComparer<T>) 使用指定比较器对List进行排序
(3)Sort(int index, int count, IComparer<T>) 在指定范围内使用指定比较器(不写,则为默认比较器)对List进行排序
第二部分 关于List之间的包含与比较
(一)包含——Contains
【注:元素之间的比较较为简单,在此不做叙述】
C#中变量可分为值类型和引用类型。值类型储存在栈中,引用类型储存在堆中,引用类型的内存地址储存在栈中。其中:泛型List的Contains在比较值类型时,直接比较值,但在比较引用类型时,比较的是引用地址。
虽然明白了Contains的比较原理,但这时候又出现了一个问题:List<T>是引用类型,string也是引用类型,那为什么在判断string是否相同时可以达到预期效果而判断List<T>时不能达到预期效果呢?针对这个问题,我们直接查看一下这两个类型所声明的变量的内存地址。
(1) 泛型类型为List<T>【此时,内部储存的元素为集合】
可以发现:
a. 两个字符串值相同,则其内存地址相同
b. 两个List<T>值相同,但其内存地址不同
c. 当泛型类型为集合时,即使两个集合中储存的元素值相同且元素内存地址相同,但这两个集合本身的内存地址不同,即list1的地址不同于list2。而根据Contains的查找规律,无法将这两个集合视为同一个集合。
所以我们所认为的集合相同是内部元素相同,而C#所认为的集合相同是内存地址的相同。
(2)泛型类型为string【此时,内部储存的元素为字符串】
内部元素不同:
内部元素相同:
可以发现:
a. 两个字符串值相同,则其内存地址相同
b. 当泛型类型为字符串时,只有两个字符串的内存地址相同,即str1的地址等同于str2,所以可将这两个元素视为同一个元素。
(3)泛型类型为自定义类型【此时,内部储存的元素为自定义对象】
通过上面这个例子可以发现:
a. 两个自定义对象值相同,但其内存地址不同
b. 对象再传递时,传递的是内存地址
c. 当泛型类型为自定义对象类型时,只有两个对象的内存地址相同时,才判定这两个对象是同一个对象。
小结
至此,我们得出List. Contains()方法的执行原理:
a. 在查找是否包含时,查找的是针对定义的泛型T,所存储的对象,与对象中再存储的元素无关。
b. 在查找是否包含值类型时,直接比较值;查找引用类型时,先比较内存地址,若内存地址相同则返回True;否则再比较值。
(二)比较——Equals
(1)泛型类型为List<T>【此时,内部储存的元素为集合】
(2)泛型类型为string【此时,内部储存的元素为字符串】
内部元素不同:
内部元素相同:
直接使集合本身相同:
(3)泛型类型为自定义类型【此时,内部储存的元素为自定义对象】
小结
a. 通过以上两个例子,我们可以总结出List. Equals()方法的执行原理:在比较值类型时,直接比较值;在比较引用类型时,只比较内存地址。
回到开头的题目
为什么第一种方法是错的,因为执行Contains操作的对象是是两个集合,虽然两个集合内部存储元素相同,但两个集合本身的内存地址是不同的,所以总会被判定为是不同的元素,因而不能用于判重;
第二种方法使用字符串,操作对象即为字符串,相同的字符串值,内存地址相同,所以可以用来判重。
总结
a. 不论是Contains方法还是Equals方法,首先要明确操作的对象,即要查找或比较的是哪两个对象。
b. 确定对象后,判断其内存地址是否相同,相同返回True,不同返回False。
c. 一般地,引用类型传递时,传递的是内存地址,且相同值的引用类型内存地址不同;特别地,当字符串值相同时,内存地址也相同。
【感谢您可以抽出时间阅读到这里,第一篇博客可能会有许多不妥之处;受限于水平,许多地方可能存在错误,还请各位大佬留言指正,请见谅,谢谢!】
C# 有关List<T>的Contains与Equals方法的更多相关文章
- List<T>Find方法,FindAll方法,Contains方法,Equals方法
假如传入的T是一个类, List<MessageInfos> MessageInfos = new List<MessageInfos>(); MessageInfos= Me ...
- [java] 更好的书写equals方法-汇率换算器的实现(4)
[java] 更好的书写equals方法-汇率换算器的实现(4) // */ // ]]> [java] 更好的书写equals方法-汇率换算器的实现(4) Table of Content ...
- java中的equals()方法重写
如何java中默认的equals方法跟实际不符的话,需要重写equals方法.例如: public class TestEquals { public static void main(String[ ...
- java提高篇(十三)-----equals()方法总结
equals() 超类Object中有这个equals()方法,该方法主要用于比较两个对象是否相等.该方法的源码如下: public boolean equals(Object obj) { retu ...
- Java中“==”与equals方法的区别
1. 用“==”比较两个变量,如果两个变量是基本类型变量,且都是数值类,则值相等就返回true 如果两个变量是引用型变量,则两个对象的地址一样,即指向同一个对象,则返回true 2.equals:St ...
- equals()方法
equals()方法是根类Object中的一个方法,子类可以根据需要重写该方法(比如:String类). 一.Object类中的equals()方法实现如下: public boolean equal ...
- C#中==与Equals方法的区别
using System;using System.Collections.Generic;using System.Linq;using System.Text; namespace Console ...
- java重写equals方法
@Override public int hashCode() { return task.getId(); } @Override public boolean equals(Object obj) ...
- 重写Object类中的equals方法
Object是所有类的父亲,这个类有很多方法,我们都可以直接调用,但有些方法并不适合,例如下面的student类 public class Student { //姓名.学号.年纪 private S ...
- java中hashCode方法与equals方法的用法总结
首先,想要明白hashCode的作用,必须要先知道Java中的集合. 总的来说,Java中的集合(Collection)有两类,一类是List,再有一类是Set. 前者集合内的元素是有序的,元素可以重 ...
随机推荐
- char型变量中能不能存储一个中文字符?为什么?
char型变量是用来存储Unicode编码的字符的,Unicode编码字符集中包含了汉字,因此char型变量中可以存储汉字.不过,如果某个特殊的汉字没有被包含在Unicode编码字符集中,那么,这个c ...
- 四种类型的数据节点 Znode ?
1.PERSISTENT-持久节点 除非手动删除,否则节点一直存在于 Zookeeper 上 2.EPHEMERAL-临时节点 临时节点的生命周期与客户端会话绑定,一旦客户端会话失效(客户端与 zoo ...
- XML 文档定义有几种形式?它们之间有何本质区别?解析 XML 文档有哪几种方式?
XML 文档定义分为 DTD 和 Schema 两种形式,二者都是对 XML 语法的约束,其 本质区别在于 Schema 本身也是一个 XML 文件,可以被 XML 解析器解析,而且 可以为 XML ...
- java-設計模式-原型模式
原型模式 是一种创建型设计模式, 使你能够复制已有对象, 而又无需使代码依赖它们所属的类. 問題: 如果我們要複製一個類實例: 首先, 你必须新建一个属于相同类的对象. 然后, 你必须遍历原始对象的所 ...
- memcacuery cache 相比,有什么优缺点?
把 memcached 引入应用中,还是需要不少工作量的.MySQL 有个使用方便的 query cache,可以自动地缓存 SQL 查询的结果,被缓存的 SQL 查询可以被反复 地快速执行.Memc ...
- chubby 是什么,和 zookeeper 比你怎么看?
chubby 是 google 的,完全实现 paxos 算法,不开源.zookeeper 是 chubby的开源实现,使用 zab 协议,paxos 算法的变种.
- 机器学习之linear_model (线性回归算法模型)
1.matplotlib 首先看一下这个静态图绘制模块 静态图形处理 数据分析三剑客 Numpy : 主要为了给pandas提供数据源 pandas : 更重要的数据结构 matplotlib : 静 ...
- resin服务之三---独立resin的配置
独立resin的配置 关掉httpd服务: [root@data-1-1 ~]# killall httpd [root@data-1-1 ~]# lsof -i :80 ------>h ...
- Numpy实现多项式曲线拟合
Numpy实现多项式曲线拟合 这里可以对比matlab中的拟合方式看看matlab拟合函数的三种方法,和第一种方式很像 问题定义:对于一堆数据点(x, y),能否只根据这些数据,找出一个函数,使得函数 ...
- React系列——websocket群聊系统在react的实现
前奏 这篇文章仅对不熟悉在react中使用socket.io的人.以及websocket入门者有帮助. 下面这个动态图展示的聊天系统是用react+express+websocket搭建的,很模糊吧, ...