转载请注明出处:https://www.cnblogs.com/funnyzpc/p/16421743.html
对于集合类删除元素是常有的需求,非常常见;如果是惯常的删除方式就没有写本篇博客的必要了,本篇博客不光分析删除可能导致的问题,也会从源码层面分析为何需要借用迭代器删除,同时也会给出不同业务形态下的删除方式等,有兴趣的往下看看囖?
这是两种不同的业务形态,如果是确定待删除元素的索引位置或元素值且只删除一个元素的情况下,一般是直接调用ArrayList下的remove删除方法,但这不是本篇重点
public static void main(String[] args) {
ArrayList arr = new ArrayList();
arr.add("a");
arr.add("b");
arr.add("c");
arr.add("d");
arr.add("e");
System.out.println(arr);
arr.remove("c");// remove c
arr.remove(3);// remove d
System.out.println(arr);
}
还一种情况是删除多个元素,一般不能确定待删除元素的索引位置,这样就需要在循环内删除了;
public static void main(String[] args) {
ArrayList arr = new ArrayList();
arr.add("a");
arr.add("b");
arr.add("c");
arr.add("d");
arr.add("e");
arr.add("d");
System.out.println(arr);
for(int i=0;i<arr.size();i++){
if("d".equals(arr.get(i))){
arr.remove("d");
}
}
System.out.println(arr);
}
(错误用法一)
public static void main(String[] args) {
ArrayList arr = new ArrayList();
arr.add("a");
arr.add("b");
arr.add("c");
arr.add("d");
arr.add("e");
arr.add("d");
System.out.println(arr);
for(Object item:arr){
if("d".equals(item)){
arr.remove(item);
}
}
}
(错误用法二)
public static void main(String[] args) {
ArrayList arr = new ArrayList();
arr.add("a");
arr.add("b");
arr.add("c");
arr.add("d");
arr.add("e");
arr.add("d");
System.out.println(arr);
arr.forEach(item->{
if("d".equals(item)){
arr.remove("d");
}
});
}
虽然均为删除,但是简化版的for循环还有函数式循环下就不能删除呢?往下看...
这是ArrayList的remove源码:
public E remove(int index) {
rangeCheck(index);
modCount++;
E oldValue = elementData(index);
int numMoved = size - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
elementData[--size] = null; // clear to let GC do its work 明确让GC做它的工作(也就是缩一个位置并置为null,切断引用了自然会被gc掉)
return oldValue;
}
这是迭代器remove源码:
public void remove() {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try {
ArrayList.this.remove(lastRet);
cursor = lastRet;
lastRet = -1;
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
对于ArrayList的remove,其实重要的一点是 --size ,而对于迭代器的remove不仅仅是调用ArrayList的删除还需要更新游标(cursor)以及当前元素索引位置(lastRet), 这时灵感就来了,是不是简化版for循环以及函数式forEach循环下的size变成了常数了呢?对,其实也就是这样,如果我将循环删除逻辑改成这样就好理解了:
public static void main(String[] args) {
ArrayList arr = new ArrayList();
arr.add("a");
arr.add("b");
arr.add("c");
arr.add("d");
arr.add("e");
arr.add("d");
System.out.println(arr);
for(int i=0;i<6;i++){
if( i==(arr.size()-1) ){
break;
}
if(arr.get(i).equals("d")){
arr.remove(i);
}
}
System.out.println(arr);
}
所以每次进入循环后均要检视下数组的size是否发生了变化,否则越界,所以不管是哪种方式的删除均要获取到最新的 size这样才能保证安全删除
如果需要保证完全的安全删除,建议您使用迭代器 iterator 或 listIterator:
public static void main(String[] args) {
ArrayList arr = new ArrayList();
arr.add("a");
arr.add("b");
arr.add("c");
arr.add("d");
arr.add("e");
arr.add("d");
System.out.println(arr);
Iterator itr = arr.iterator();
while(itr.hasNext()){
Object item = itr.next();
if("d".equals(item)){
itr.remove();
}
}
System.out.println(arr);
}
public static void main(String[] args) {
ArrayList arr = new ArrayList();
arr.add("a");
arr.add("b");
arr.add("c");
arr.add("d");
arr.add("e");
arr.add("d");
System.out.println(arr);
ListIterator itr = arr.listIterator();
while(itr.hasNext()){
Object item = itr.next();
if("d".equals(item)){
itr.remove();
}
}
System.out.println(arr);
}
如果需要在迭代器内获取当前迭代元素的索引或者
public static void main(String[] args) {
ArrayList arr = new ArrayList();
arr.add("a");
arr.add("b");
arr.add("c");
arr.add("d");
arr.add("e");
arr.add("d");
System.out.println(arr);
ListIterator lst_itr = arr.listIterator();
while(lst_itr.hasNext()){
int idx = lst_itr.nextIndex();
Object item = lst_itr.next();
System.out.println(idx+"->"+item);
if("d".equals(item)){
lst_itr.remove();
}
}
System.out.println(arr);
}
当然啰,如果既想用迭代器又想获取当前循环元素的索引位置,又想获取原数组(未删除前的)的索引位置,可以尝试这样:
public static void main(String[] args) {
ArrayList arr = new ArrayList();
arr.add("a");
arr.add("b");
arr.add("c");
arr.add("d");
arr.add("e");
arr.add("d");
System.out.println(arr);
int i = 0;
for(ListIterator lst_itr = arr.listIterator();lst_itr.hasNext();i++){
int idx = lst_itr.nextIndex();
Object item = lst_itr.next();
System.out.println(i+" -> "+idx+":"+item);
if("d".equals(item)){
lst_itr.remove();
}
}
System.out.println(arr);
}
public static void main(String[] args) {
ArrayList arr = new ArrayList();
arr.add("a");
arr.add("b");
arr.add("c");
arr.add("d");
arr.add("e");
arr.add("d");
System.out.println(arr);
arr.removeIf(item->"d".equals(item));
System.out.println(arr);
}
简化删除方式内部源码:
@Override
public boolean removeIf(Predicate<? super E> filter) {
Objects.requireNonNull(filter);
// figure out which elements are to be removed
// any exception thrown from the filter predicate at this stage
// will leave the collection unmodified
int removeCount = 0;
final BitSet removeSet = new BitSet(size);
final int expectedModCount = modCount;
final int size = this.size;
for (int i=0; modCount == expectedModCount && i < size; i++) {
@SuppressWarnings("unchecked")
final E element = (E) elementData[i];
if (filter.test(element)) {
removeSet.set(i);
removeCount++;
}
}
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
// shift surviving elements left over the spaces left by removed elements
// 将剩余的元素移到被移除的元素留下的空间上 ,也即数组瘦身
final boolean anyToRemove = removeCount > 0;
if (anyToRemove) {
final int newSize = size - removeCount;
for (int i=0, j=0; (i < size) && (j < newSize); i++, j++) {
i = removeSet.nextClearBit(i);
elementData[j] = elementData[i];
}
for (int k=newSize; k < size; k++) {
elementData[k] = null; // Let gc do its work
}
this.size = newSize;
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
modCount++;
}
return anyToRemove;
}
哈哈,是不是超复杂,内部不仅仅要维护removeCount还需要维护版本,同时元素的位置也采用特殊的方式移动,这样一来效率就似乎就不是那么高了,所以看似简单的用法内部一点儿也不简单。。。
我有一个对象has_many应呈现为xml的子对象。这不是问题。我的问题是我创建了一个Hash包含此数据,就像解析器需要它一样。但是rails自动将整个文件包含在.........我需要摆脱type="array"和我该如何处理?我没有在文档中找到任何内容。 最佳答案 我遇到了同样的问题;这是我的XML:我在用这个:entries.to_xml将散列数据转换为XML,但这会将条目的数据包装到中所以我修改了:entries.to_xml(root:"Contacts")但这仍然将转换后的XML包装在“联系人”中,将我的XML代码修改为
查看Ruby的CSV库的文档,我非常确定这是可能且简单的。我只需要使用Ruby删除CSV文件的前三列,但我没有成功运行它。 最佳答案 csv_table=CSV.read(file_path_in,:headers=>true)csv_table.delete("header_name")csv_table.to_csv#=>ThenewCSVinstringformat检查CSV::Table文档:http://ruby-doc.org/stdlib-1.9.2/libdoc/csv/rdoc/CSV/Table.html
我发现ActiveRecord::Base.transaction在复杂方法中非常有效。我想知道是否可以在如下事务中从AWSS3上传/删除文件:S3Object.transactiondo#writeintofiles#raiseanexceptionend引发异常后,每个操作都应在S3上回滚。S3Object这可能吗?? 最佳答案 虽然S3API具有批量删除功能,但它不支持事务,因为每个删除操作都可以独立于其他操作成功/失败。该API不提供任何批量上传功能(通过PUT或POST),因此每个上传操作都是通过一个独立的API调用完成的
在Ruby中是否有Gem或安全删除文件的方法?我想避免系统上可能不存在的外部程序。“安全删除”指的是覆盖文件内容。 最佳答案 如果您使用的是*nix,一个很好的方法是使用exec/open3/open4调用shred:`shred-fxuz#{filename}`http://www.gnu.org/s/coreutils/manual/html_node/shred-invocation.html检查这个类似的帖子:Writingafileshredderinpythonorruby?
查看我的Ruby代码:h=Hash.new([])h[0]=:word1h[1]=h[1]输出是:Hash={0=>:word1,1=>[:word2,:word3],2=>[:word2,:word3]}我希望有Hash={0=>:word1,1=>[:word2],2=>[:word3]}为什么要附加第二个哈希元素(数组)?如何将新数组元素附加到第三个哈希元素? 最佳答案 如果您提供单个值作为Hash.new的参数(例如Hash.new([]),完全相同的对象将用作每个缺失键的默认值。这就是您所拥有的,那是你不想要的。您可以改用
我正在尝试找到一种方法来规范化字符串以将其作为文件名传递。到目前为止我有这个:my_string.mb_chars.normalize(:kd).gsub(/[^\x00-\x7F]/n,'').downcase.gsub(/[^a-z]/,'_')但第一个问题:-字符。我猜这个方法还有更多问题。我不控制名称,名称字符串可以有重音符、空格和特殊字符。我想删除所有这些,用相应的字母('é'=>'e')替换重音符号,并将其余的替换为'_'字符。名字是这样的:“Prélèvements-常规”“健康证”...我希望它们像一个没有空格/特殊字符的文件名:“prelevements_routin
本文主要介绍在使用Selenium进行自动化测试或者任务时,对于使用了iframe的页面,如何定位iframe中的元素文章目录场景描述解决方案具体代码场景描述当我们在使用Selenium进行自动化测试的时候,可能会遇到一些界面或者窗体是使用HTML的iframe标签进行承载的。对于iframe中的标签,如果直接查找是无法找到的,会抛出没有找到元素的异常。比如近在咫尺的例子就是,CSDN的登录窗体就是使用的iframe,大家可以尝试通过F12开发者模式查看到的tag_name,class_name,id或者xpath来定位中的页面元素,会抛出NoSuchElementException异常。解决
我是HanamiWorld的新人。我已经写了这段代码:moduleWeb::Views::HomeclassIndexincludeWeb::ViewincludeHanami::Helpers::HtmlHelperdeftitlehtml.headerdoh1'Testsearchengine',id:'title'hrdiv(id:'test')dolink_to('Home',"/",class:'mnu_orizontal')link_to('About',"/",class:'mnu_orizontal')endendendendend我在模板上调用了title方法。htm
我去了这个website查看Rails5.0.0和Rails5.1.1之间的区别为什么5.1.1不再包含:config/initializers/session_store.rb?谢谢 最佳答案 这是删除它的提交:Setupdefaultsessionstoreinternally,nolongerthroughanapplicationinitializer总而言之,新应用没有该初始化器,session存储默认设置为cookie存储。即与在该初始值设定项的生成版本中指定的值相同。 关于
啊,正则表达式有点困惑。我正在尝试删除字符串末尾所有可能的标点符号:ifstr[str.length-1]=='?'||str[str.length-1]=='.'||str[str.length-1]=='!'orstr[str.length-1]==','||str[str.length-1]==';'str.chomp!end我相信有更好的方法来做到这一点。有什么指点吗? 最佳答案 str.sub!(/[?.!,;]?$/,'')[?.!,;]-字符类。匹配这5个字符中的任何一个(注意,。在字符类中并不特殊)?-前一个字符或组