java集合类,ArrayList、HashSet 、LinkedLlist

 

ArrayList是实现了基于动态数组的数据结构,LinkedList基于链表的数据结构。

读写效率

HashSet读写是最慢的,因为HashSet每次add要判断hashcode,HashSet两种循环中iterator 方式不稳定,不过总是比foreach要快一点。ArrayList读写效率其次,在ArrayList中间插入或删除一个元素,要改变整个集合中该元素后面所有元素的下标位置。LinkedList读写速度最快,LinkedList的中间插入或删除一个元素的开销是固定的,只需要对插入位置前后的元素指针进行修改。

去除集合内部重复元素

如果要用HashSet对集合中元素实现去重,不推荐这种做法,因为hash算法的速度比遍历一个ArrayList要慢得多。即使在ArrayList中进行两次循环,所消耗的时间仍然快于直接使用HashSet的构造方法。

String stocks="001,002,003,002";
LinkedHashSet stocksSet=new LinkedHashSet<(Arrays.asList(stocks.split(",")));
return String.join(",",stocksSet);
           

 ArrayList中的remove方法会将元素前移一个位置,这里使用倒序删除避免漏掉元素或者报并发错误。

String stocks="001,002,003,002";
ArrayList list=new ArrayList<>(Arrays.asList(stocks.split(",")));
for  ( int  i  =   0 ; i  <  list.size()  -   1 ; i ++ )  {       
   for  ( int  j  =  list.size()  -   1 ; j  >  i; j -- )  {       
      if  (list.get(j).equals(list.get(i)))  {       
              list.remove(j);       
       }        
    }        
}        
return String.join(",",list);      

 

访问元素

ArrayList的内部实现是基于基础的对象数组的,因此,它使用get方法访问列表中的任意一个元素时(random-access),它的速度要比LinkedList快。当然也包括使用 for(int i<0;i<list.size();i++) 这种循环方式。LinkedList中的get方法是按照顺序从列表的一端开始检查,直到另外一端。对LinkedList而言,利用Collections.reverse方法对列表进行顺序反转时,LinkedList性能要好些。 

空间复杂度

ArrayList和LinkedList在空间复杂度上都具有一定的空间上的冗余。ArrayList的空间浪费主要体现在在list列表的结尾预留一定的容量空间,而LinkedList的空间花费则体现在它的每一个元素都需要消耗相当的空间,在LinkedList中有一个私有的内部类,定义如下:

private static class Entry {
         Object element; 
         Entry next; 
         Entry previous; 
     }

每个Entry对象reference列表中的一个元素,同时还有在LinkedList中它的上一个元素和下一个元素。一个有1000个元素的LinkedList对象将有1000个链接在一起的Entry对象,每个对象都对应于列表中的一个元素。这样的话,在一个LinkedList结构中将有一个很大的空间开销,因为它要存储这1000个Entity对象的相关信息。

ArrayList使用一个内置的数组来存储元素,这个数组的起始容量是10.当数组需要增长时,新的容量按如下公式获得:新容量=(旧容量*3)/2+1,也就是说每一次容量大概会增长50%。这就意味着,如果你有一个包含大量元素的ArrayList对象,那么最终将有很大的空间会被浪费掉,这个浪费是由ArrayList的工作方式本身造成的。如果没有足够的空间来存放新的元素,数组将不得不被重新进行分配以便能够增加新的元素。对数组进行重新分配,将会导致性能急剧下降。如果我们知道一个ArrayList将会有多少个元素,我们可以通过构造方法来指定容量。我们还可以通过trimToSize方法在ArrayList分配完毕之后去掉浪费掉的空间。

ArrayList和LinkedList循环遍历方式的性能分析

1、for-each

List<String> testList = new ArrayList<String>();

for (String tmp : testList)

{  

  //use tmp;

}


2、迭代器方式这种遍历方式是最常用的遍历方式,因为书写比较方便,而且不需要考虑数组越界的问题,Effective-Java中推荐使用此种写法遍历。

List<String> testList = new ArrayList<String>();

for (Iterator<String> iterator = testList.iterator(); iterator.hasNext();)

{

    //String tmp = iterator.next();

}

 

3、下标递增或递减循环

List<String> testList = new ArrayList<String>();

for (int i = 0; i < testList.size(); i++;)

{

    //String tmp = testList.get(i);

}


以上三种遍历方式是在使用list时最常用的方式,那么这三种方式在遍历的速度已经性能上又有什么区别呢?我们从数据的底层实现上来进行分析。下标递增或者递减循环是最早接触到的遍历方式,会经常出现数组越界的问题。

List底层储存都是使用数组来进行存储的,ArrayList是直接通过数组来进行存储,而LinkedList则是使用数组模拟指针,来实现链表的方式,因此从这里就可以总结出,ArrayList在使用下标的方式循环遍历的时候性能最好,通过下标可以直接取数据,速度最快。而LinkedList因为有一层指针,无法直接取到对应的下标,因此在使用下标遍历时就需要计算对应的下面是哪个元素,从指针的头一步一步的走,所以效率就很低。想到指针就会联想到迭代器,迭代器可以指向下一个元素,而迭代器就是使用指针来实现的,因此LinkedList在使用迭代器遍历时会效率最高,迭代器直接通过LinkedList的指针进行遍历,ArrayList在使用迭代器时,因为要通过ArrayList先生成指针,因此效率就会低于下标方式,而for-each又是在迭代器基础上又进行了封装,因此效率会更低一点,但是会很接近迭代器。

在进行list遍历时,如果是对ArrayList进行遍历,推荐使用下标方式,如果是LinkedList则推荐使用迭代器方式。


 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值