号外!RandomAccess它是个空接口!

本文深入解析了RandomAccess接口,揭示其作为标记接口的作用,以及在List实现中的意义。通过对比ArrayList与LinkedList,阐述了不同数据结构对算法效率的影响。

号外!RandomAccess它是个空接口

关于RamdomAccess接口,比较正规的解释为:
用于标明实现该接口的List支持快速随机访问,主要目的是使算法能够在随机和顺序访问的list中表现的更加高效。

那么,接下来,Ted就敲起键盘,带着你撸一撸它,感受一下它的触感。

!它是一个空接口 标记接口

查看源码,发现是这样的

public interface RandomAccess {
}  

什么都没有啊,那它的后代们(实现类)继承它的衣钵(方法)的机会都没有,那它老人家,用来干什么呢? 这种接口称为标记接口,如果某个类implements它,只是标记这个类和它有关系,仅此。

! 标记接口的具体使用

比较经常会用到的就是判断一个list集合,它是否是RandomAccess的一个实现,如果是,do somting 如果不是,do someting else.
此刻我们用到Collections类(内部有很多对集合操作的方法)举例 ,看代码

public static <T>
int binarySearch(List<? extends Comparable<? super T>> list, T key) {
if (list instanceof RandomAccess || list.size()<BINARYSEARCH_THRESHOLD)
return Collections.indexedBinarySearch(list, key);
else
return Collections.iteratorBinarySearch(list, key);
}

在binarySearch()方法中,它要判断传入的list 是否RamdomAccess的实例,如果是,调用indexedBinarySearch()方法,如果不是,那么调用iteratorBinarySearch()方法
此时,我们就可以看出,RandomAccess接口的作用,就是为了标记,用作实例判断 。
但此时,引申出2个问题

  • 为什么传List 为(List<? extends Comparable<? super T>> list ) 这样奇怪的泛型结构 ?

  • 哪些list 属于randomAccess ,哪些不是呢 ?

于是,我带着问题,遨游在度娘的怀抱中。。。。。

第1个问题,也不难理解,我们一般申明一个list集合时会这样,比如:List <Dog>
第1个?号,它就是指代这个具体的类型(如Dog),而且这个具体类型要extends Comparable,说明这个具体类型一定要实现Comparable接口(java APi规定 重写Comparable接口后才能对自定义对象进行比较,可具体查看本人博文https://blog.youkuaiyun.com/ted_cs/article/details/82713706有详细介绍)
Comparable<? super T >这句话,说明Compareable接口,也要传入规定类型,而?我们之前已经清楚 ,是指代List<Dog >中的Dog对象,那么Dog对象的super T ,T应该指的是Dog对象的父类了。

那为什么还要通过super,引出一个父类对象呢,因为父子类间也可以通过CompareTo()方法来比较,比的一般是它们公有的属性的自然顺序。
但因为我自定义的Dog类,我没有自定义父类,但Comparable后面如果不加泛型,又会报错,那么我也可以这样写:

  public class Dog implements Comparable<Dog>{
  重写CompareTo()方法
  }   

至于,第2个问题嘛,其实我们可以翻看java IPA就知道哪些list实现了RandmoAccess接口,
如ArraysList 实现了, 而LinkedList 没有 。

哎,讲到这俩个类,感觉我又要扯出来数据结构才能讲清楚,算了吧,我就简单过一下,然后我们看代码再来感悟一下。

ArraysList实现基础是Array数组,是有序且占用连续的内存;LinkedList实现是Link链表结构,是有序占用非连序内存;ArraysList是数组集合,有索引,可以根据索引快速get到对应数值,而LinkedList,虽然也有索引的概念,但可以理解为隐式的,只是说可以通过索引可以达到正序或倒序的查找结果,有一定优化作用,但本质上,还是一个一个Entry根据双向“指针”的去查找下一个Entry.
估计我讲到这里,有人会蒙圈,没关系,送上救命药给你,请查看 https://blog.youkuaiyun.com/ted_cs/article/details/82848350
最终得到的结果是,随着LinkedList的size增加,get方法的有很复杂,远不如AraysList的get的效率。

那回到我们问题,既然实现RandmoAccess接口,可以支持什么快速访问,什么高效算法什么的,那给人家LinkedList也实现好了啊,这们好的东西,怎么可以不分享?
那是因为本身实现快速访问,高效算法什么的,其实是针对数组结构的,因为有索引,让其有了实现的可能 ,而LinkedList不是数组实现的,你丫,就算给你实现这样接口,你丫也没有数组的功能啊,所以数据结构才是问题的根本!!!

那么,理解了这一层,我们再来过过代码,深入感受下

indexedBinarySearch 方法:

private static <T>
int indexedBinarySearch(List<? extends Comparable<? super T>> list, T key) {
int low = 0;
int high = list.size()-1;
 
while (low <= high) {
int mid = (low + high) >>> 1;
Comparable<? super T> midVal = list.get(mid);
int cmp = midVal.compareTo(key);
 
if (cmp < 0)
low = mid + 1;
else if (cmp > 0)
high = mid - 1;
else
return mid; // key found
}
return -(low + 1);  // key not found
}
  • 请注意,这是一个根据值来查索引的方法
  • 实现二分法查询,list中的对象一定要实现Comparable接口

通过此代码可以看出,其实这就是一个遍历数组的过程,通过索引来获得对应的对象值,因为是数组结构,get方法的效率高

indexedBinarySearch 方法:

private static <T>
int iteratorBinarySearch(List<? extends Comparable<? super T>> list, T key)
{
int low = 0;
int high = list.size()-1;
ListIterator<? extends Comparable<? super T>> i = list.listIterator();
 
while (low <= high) {
int mid = (low + high) >>> 1;
Comparable<? super T> midVal = get(i, mid);
int cmp = midVal.compareTo(key);
 
if (cmp < 0)
low = mid + 1;
else if (cmp > 0)
high = mid - 1;
else
return mid; // key found
}
return -(low + 1);  // key not found
}  

通过上面的的代码,我们发现link结构的list,调用get方法,传入了ListInterator对象i,和索引mid,
但我们知道,对于Link结构的集合的话,通过iterator遍历是要比普通for循环遍历高效的,所以,判断list没有实现RamdomAccess接口后,其实也相当于知道它不是数组结构,那么就会调用迭代器来进行遍历,提高性能。

讲到这里了,我们应该深刻的认识到

  • linkedList集合,遍历时,由其是huge size时,千万不要用普通for循环,太low效了!!
    End!

在这里插入图片描述

<think> 首先,用户的问题是关于正则表达式 [^*] 的含义。他们在问:"我想了解正则表达式 [^*] 的含义 请问正则表达式 [^*] 的含义"。所以,我需要解释这个特定的正则表达式。 现在,回顾系统级指令: - 所有行内数学表达式必须使用 $...$ 格式。 - 独立公式必须使用 $$...$$ 格式并单独成段。 - LaTeX语法正确。 - 使用中文回答。 - 生成相关问题。 - 回答中引用的段落末尾自然地添加引用标识。 用户提供了几个引用: - 引用[1]:关于Linux正则表达式的一般知识。 - 引用[2]:解释正则表达式中^的用法,特别是 [^abc] 表示否定字符类。 - 引用[3]:解释了正则表达式*的用法,包括它是量词,需要前置元素。 - 引用[4]:解释了.*, .*?, .+? 的含义,包括贪婪模式。 用户的问题直接涉及 [^*],这与引用[2]特别相关,因为它提到了字符类中的^表示否定。 关键点: - 在正则表达式中,中括号 [ ] 定义字符类,匹配其中任何一个字符。 - 在这个字符类中,^ 作为第一个字符时,表示否定,即匹配不在指定字符集中的任何字符。 - 所以,[^*] 表示匹配任何不是星号 * 的字符。 - 这里的 ^ 在字符类开头,表示否定。 - * 是字面上的星号字符,因为它不是在中括号外或其他特殊位置。 - 因此,[^*] 匹配任何单个字符,除了星号 *。 从引用[2]: - "[^abc] // 表示“找到一个插入符或者a或者b或者c”。" 这是错误的;引用说 "[^abc] // 表示“找到一个插入符或者a或者b或者c”",但实际上 [^abc] 表示匹配任何不是a、b或c的字符。引用中可能有个打字错误,因为它说 "插入符",但应该是字符类否定。 - 它在后面总结:"1 .只要是”^”这个字符是在中括号”[ ]”中被使用的话就是表示字符类的否定,如果不是的话就是表示限定开头。2 .这里说的是直接在”[ ]”中使用,不包括嵌套使用。3 .其实也就是说”[ ]”代表的是一个字符集,”^”只有在字符集中才是反向字符集的意思。[^2]" 所以,纠正引用[2],[^abc] 表示否定字符类,匹配任何不在a、b、c中的字符。 应用到 [^*],同样地: - [^*] 表示匹配任何不是星号 * 的字符。 其他引用: - 引用[3] 解释了 * 是量词,但在字符类中,像 [^*],* 是字面字符,因为在字符类中没有特殊含义。 - 引用[4] 解释了 .* 等,但不直接相关。 因此,对于 [^*] 的含义: - "[^*]" 是一个字符类否定。 - 它匹配任何单个字符,除了星号 (*)。 现在,构建我的回答: - 使用中文回答。 - 结构清晰:先直接回答问题,然后逐步解释。 - 添加引用:自然地添加引用标识在相关部分末尾。 - 生成相关问题:在回答结束时提供相关问题。 回答结构: 1. **直接回答**:正则表达式 [^*] 的含义是匹配任何不是星号 * 的单个字符。 2. **解释**: - 在正则表达式中,中括号 [ ] 定义一个字符集。 - 当 ^ 出现在字符集开头时,它表示否定(即匹配不在指定字符集中的字符)。 - 在 [^*] 中,^ 表示否定,* 是字面上的星号字符。 - 所以,它匹配除了 * 之外的任何字符。 3. **引用**参考:根据引用[2],^ 在字符类中表示否定。 4. **例子**:提供简单例子来说明用法。 - 例如,在字符串 "a*b" 中,使用 [^*] 匹配 'a' 或 'b',但不匹配 '*'。 5. **注意事项**:强调这在基础正则表达式和扩展正则表达式中通常有效。 6. **相关问题**:结束时生成2-4个相关问题。 生成相关问题: - 基于主题: 1. 其他正则表达式否定字符类的例子是什么? 2. 如何在正则表达式中匹配字面星号? 3. 正则表达式中的字符类有哪些常见用法? 4. 否定字符类和锚点的区别? 确保LaTeX:正则表达式本身不是数学,但如果有数学元素,使用 $...$。这里,[^*] 是正则表达式符号,不需要LaTeX数学模式,直接用代码块或内联代码表示。 在回答中,使用代码块或内联表示正则表达式: - 例如:正则表达式 `[^*]` 表示... 最终回答大纲: - 开头:正则表达式 `[^*]` 的含义是匹配任何非星号 (*) 的单个字符。 - 解释:详细解释字符类和否定。 - 引用:自然地添加 [^2] 在解释后。 - 例子:给出示例。 - 生成相关问题。 </think> ### 正则表达式 `[^*]` 的含义 正则表达式 `[^*]` 的含义是:匹配任何**单个字符**,但**排除星号 (*)**。这是一个**否定字符类**(negative character class),在正则表达式引擎中广泛支持(包括基础正则表达式和扩展正则表达式)。下面我将逐步解释其工作原理和使用场景,帮助你清晰地理解。 #### 1. **字符类的基本概念** - 在正则表达式中,中括号 `[ ]` 定义一个字符集(character class),用于匹配其中指定的任何一个字符。 - 当 `^` 符号作为字符集的**第一个字符**出现时(例如 `[^...]`),它表示**否定**(negation),即匹配**不在**后续指定字符集中的任何字符。这被称为否定字符类[^2]。 - 在 `[^*]` 中: - `^` 位于开头,表示否定。 - `*` 被视为**字面上的星号字符**(literal asterisk),因为在字符集内部,`*` 通常不具有量词的特殊含义(除非在特定引擎中处理)。 - 因此,`[^*]` 匹配单个字符:任何字符都行,但**不能是星号 (*)**。 #### 2. **为什么 `[^*]` 表示排除星号** - 正则表达式中,符号的含义依赖于上下文: - 在字符集 `[ ]` 外,`^` 可能表示行首锚点(如 `^abc` 匹配以 "abc" 开头的行),而 `*` 表示量词(重复前一个元素零次或多次)[^2][^3]。 - 但在字符集内部(如 `[^*]`),规则不同:`^` 仅在开头时表示否定,而 `*` 被解释为普通字符(不是量词)[^2]。 - 示例解析: - 正则表达式 `[^*]` 尝试匹配输入字符串中的每个字符。 - 如果字符是星号 (*),则不匹配(因为它被排除)。 - 如果字符是其他任何符号(如字母、数字或空格),则匹配成功。 - 例如: - 在字符串 `"a*b"` 中: - `"a"` 匹配 `[^*]`(因为不是 *)。 - `"*"` 不匹配 `[^*]`。 - `"b"` 匹配 `[^*]`。 - 使用 `grep` 命令:`echo "a*b" | grep -o '[^*]'` 输出 `a` 和 `b`(跳过 *)。 #### 3. **使用场景与注意事项** - **常见用途**: - 过滤星号:在处理配置文件、代码或文本时,排除注释或特定符号(如 `/* comment */` 中匹配非星号的部分)。 - 结合其他模式:常与量词(如 `+` 或 `*`)结合使用,例如 `[^*]+` 匹配一个或多个非星号字符序列。 - **注意事项**: - `[^*]` 只匹配**单个字符**。要匹配多个字符,需结合量词(如 `[^*]+` 用于一个或多个)。 - 在大多数正则表达式引擎(如 Linux 的 `grep` 或 `ripgrep`)中,`[^*]` 的行为一致于基础正则表达式(BRE)和扩展正则表达式(ERE)[^1][^2]。 - 避免误解:`[^*]` 中的 `^` 必须是字符集的第一个字符才能表示否定;如果是 `[*^]`,则表示匹配字面 * 或 ^(无否定)。 #### 4. **实际示例** - **场景**:从字符串中提取非星号部分。 - 输入:`"test*123"`。 - 正则表达式:`[^*]+`(匹配一个或多个非星号字符)。 - 匹配结果:`"test"` 和 `"123"`(跳过 *)。 - 在Shell中测试(使用 `grep`): ```bash echo "hello*world" | grep -E -o '[^*]+' # 输出:hello # 输出:world ``` 此命令使用 `-o` 输出每个匹配项,`[^*]+` 匹配连续的星号字符序列。 正则表达式 `[^*]` 是处理文本过滤时的实用工具,特别是在需要排除特定符号的场景中[^1][^2]。如果您有特定应用需求(如解析代码),我可以提供更深入的示例说明。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值