java基础——Collection

(一)首先说说Collection的作用:

   1: Collection(集合) 可以用来存储对象的数组列表,相对于对象数组,collection不需要初始化的去定义大小,更加灵活,而且相对于数组来说,数组只能存储相同的数据类型,而collection可以存储不同的数据类型(虽然可能存在安全隐患)。

   2:说说java中collection的一些内容:

     public interface Collection<E> extends Iterable<E>

     通过查看jdk我们可以知道,Collection 实际上是一个接口,同时他继承了另外一个接口 Iterable(Iterable 迭代器接口,后续会讲到),既然知道Collection作为接口,所以无法对他进行实例化操作,但是我们可以通过其具体的实现子类来实现Collection接口。其声明中的<E> 叫做“泛型”,前面提到Collection可以用来存储不同数据类型的元素,但是可能会存在安全隐患(即可能会出现在编译程序的时候程序并不报错,但是在运行时,程序可能会出现错误) ,所以java采用了泛型<E>来限定放置在Collection的元素的数据类型。

    由于我们存储数据的一些要求不同,比如我们需要存储的数据不重复,有序(读取数据时,和存储数据的顺序一样),因为需求不一样,java对Colletion进行了分类,定义了另外两个子接口List和Set 来继承了Colletion,来满足对于存储不同需求的要求。

  Collection具体的方法除了默认的default 接口方法外,都是 抽象方法,其具体实现都需要其最终实现子类去实现,具体的实现这里也不多做概述,大家可以参考jdk

(二) List

      前面说了,为了存储数据的不同需求,java中定义了List接口和Set接口;

   public interface List<E> extends Collection<E>

  通过查询jdk可以看到List(有序集合,又称为序列) 是继承Collection的接口,可以用来存储重复以及需要有序(读取数据时,和存储数据的顺序一样)的数据集合。所以相对于Collection而言,List具有其特有的功能,具体功能实现请参考jdk。作为接口,其方法也和Collection一样,除了默认方法外,其方法都是抽象方法,具体的实现需要依靠其具体实现子类。而由于List 序列存储数据的数据结构不同,其有几种不同的实现子类,这里主要介绍其中3个:ArrayList、Vector、LinkedList

    1:ArrayList

     public class ArrayList<E>
     extends AbstractList<E>
     implements List<E>, RandomAccess, Cloneable, Serializable

    根据jdk中ArrayList的定义可以看到,它是一个继承于AbstractList的同时实现了List<E>的一个具体类,那么我们就可以通过他来具体实现Colletion和List的方法,要对于类中的非静态方法,需要我们构建类的对象才能使用,java中提供了3种ArrayList

的构造方法来创建ArrayList的对象。而对于ArrayList 的具体方法和构造方法不做概述,具体参考jdk。这里说下,前面提到List由于其数据结构不同,才分为不同的实现子类,那么ArrayList的存储结构是什么呢?实际上ArrayList的存储结构是数组,所以ArrayList具有数组存储数据的特点,就是:查询快,增删慢,同时他还有一个特点,就是线程不安全

2:Vector

   public class Vector<E>
   extends AbstractList<E>
   implements List<E>, RandomAccess, Cloneable, Serializable 

     根据其定义我们可以发现,其定义实际上除了名字和ArrayList不一样外,其他都一样的,但Vector和ArrayList有什么区别呢。java中创建Vector其实主要用来解决线程不安全的问题,面对并发操作时,Vector会抛出并发异常。所以相对于ArrayList,其实其数据结构同样是数据,具有:查询快,增删慢的特点,同时线程更加安全

3:LinkedList

    public class LinkedList<E>
    extends AbstractSequentialList<E>
    implements List<E>, Deque<E>, Cloneable, Serializable

     通过查看其定义,可以知道,相对于ArrayList和Vector 它继承的父类是发生改变了的,实际上是其存储结构与ArrayList和Vector不同,发生了改变,其数据结构是双循环链表,它具有链表的数据存储特点:增删快,查询满,同时,和ArrayList一样,使用它时也可能造成线程不安全。

————————————————————————————————————————————————————————

所以在考虑,具体使用哪个类的时候,我们需要根据自己的需求来确认,如果需要确保线程的安全,我们可以用Vector,如果需要进行增删的操作更多,优先考虑LinkedList,需要查询的操作更多,考虑使用ArrayList,更多的时候,我们优先使用ArrayList,同时,各个类虽然都属于集合的子类,具有很多的共同方法,但也有其特定的功能,比如LinkedList的addFist()、removeFirst()等方法也需要了解。

(三)Set

   public interface Set<E>

   extends Collection<E>

     相比于List 用来存储重复,有序的数据,Set是被Java创造用来存储不允许重复的元素的集合接口,而其其存储的数据可能是无序的(即读取到的数据可能和存储数据进来时候是不一样的)。作为接口,他的具体实现类也有很多,根据其具体子类的数据结构不同,我主要了解了以下3种实现类:HashSet、LinkedHashSet、TreeSet。

1:HashSet

  • public class HashSet<E>
    extends AbstractSet<E>
    implements Set<E>, Cloneable, Serializable
  • 通过查看jdk,我们可以看到HashSet是一个继承于AbstractSet,同时实现了Set接口的 一个具体类,其实可以通过查看jdk我们发现HashSet 以及Set其实具有的成员方法和Colletion差不多,但是Set 具有存储不重复的数据的特点,这是为什么呢,他是怎么实现的呢?原因就是HashSet他的数据结构不同,他属于哈希表的数据结构,哈希表实际是一个存储元素为链表的数组。我们通过构造方法构造一个HashSet的对象,在向对象增加元素时,调用其add方法
  • public boolean add(E e) {
            return map.put(e, PRESENT)==null;
        }
  • 我们发现它其实调用的是Map.put方法,map.put方法就是将元素经过一系列的处理,在处理过程中,通过hashCode()、equals()来判断插入的元素是否已经存在,如果存在,将会用新的元素替换原来的元素(如果地图先前包含了该键的映射,则替换旧值。)因此HashSet集合存入的数据是不重复,而且读取数据时和存储数据时候的顺序是不一样的。而其中判断是否存在的关键就是在于我们用HashSet存储的对象的HashCode()、equals()。对于我们常用的基本数据类的封装类型(如String/Integer/Byte/Character/Long/Short/Double/Float)都重写了HashCode()、equals()方法,而对于我们自己写的类的时候,在用HashSet来存储数据时,我们写的类直接继承的是Object的HashCode()、equals()方法,是无法来判断是否相同的,我们需要重写这两个方法,才能让HashSet不重复的存储这些数据,但通常为我们可以直接使用编译工具来一键生成我们写的类的HashCode()、equals()方法,非常方便。

2:LinkedHashSet

  • public class LinkedHashSet<E>
    extends HashSet<E>
    implements Set<E>, Cloneable, Serializable

通过查看JDK 我们可以发现其实LinkedHashSet其实是HashSet的继承子类,那么它和HashSet有什么不同呢,我们知道hashSet存储数据时,存储的数据时无序的(读取数据时和存储数据时候的顺序是不一样的) ,所以我们无法知道存储时我们数据的存储顺序,而LinkedHashSet在jdk中的定义为:哈希表和链表实现了Set接口,具有可预测的迭代次序。即在用LinkedHashSet存储数据时,他会在不存储重复数据时,保持了存储数据时数据的顺序,即不在用新的元素替换掉旧的元素,而是保留旧的元素,新的元素不插入。

3:TreeSet

  • public class TreeSet<E>
    extends AbstractSet<E>
    implements NavigableSet<E>, Cloneable, Serializable

 根据JDK可以发现TreeSet是继承于AbstractSet 同时继承了3个接口的一个具体子类,虽然他没有直接继承Set接口,但是他继承的父类AbstractSet继承了Set接口。相比较于HashSet,他具有一个额外的功能,就是对它存储的数据实现一个排序功能。为什么他能实现排序功能呢,我们首先看TreeSet怎么添加元素。

public boolean add(E e) {
        return m.put(e, PRESENT)==null;
    }

通过其add(E e)方法我们可以知道,它其实是调用m.put方法。通过jdk查看m

  private transient NavigableMap<E,Object> m;

 发现其实m是一个接口类型的成员变量,它调用的其实是Map接口的put方法,具体是指通过TreeSet类,实现了NavigableSet<E>接口,而NavigableSet<E>的实现即基于TreeMap,所以TreeSet的元素添加功能是通过TreeMap的put()方法来添加的。而对于TreeSet,他的数据结构就是红黑树(自平衡二叉树),在添加元素时,对于相同的元素,其不进行处理,而不同的元素添加进去,所以就是存储的元素是无序(读取数据时和存储数据时候的顺序是不一样的)的。

    那么它怎么来实现对存储的数据进行排序功能呢,其原因在于,他在添加元素时,是采用的TreeMap的put()方法,在put()方法中,这里涉及了两种排序方式;

    一种是数据自然排序:什么意思呢,就是数据它本身具有排序的方式,在调用put()方法添加元素时,会对元素进行比较,此时用到的比较方式是compareTo()方法。此时是只要HashSet中的元素继承了Comparable接口,那么直接调用元素的CompareTo()方法来比较,将其添加到红黑树中(也就是HashSet中),向八大基本元素的封装类都继承了Comparable<E>接口,并重写了CompareTo()方法,所以采用自然排序,将数据存储在HashSet中,遍历时,根据红黑树的前序遍历。遍历后的数据就是已经排好序了的。

  另一种是比较器排序:什么是比较器排序,就是HashSet存储的元素继承了Comparator<E>接口,并重写了Comparator<E>接口的Compare(T o1,T o2)的方法,那么在添加元素到HashSet中时,会创建一个元素的比较器Comparator ,采用的是重写的Compare()方法来对元素进行比较。

————————————————————————————————————————————————————————

所以,在确定需要存储的数据不需要存储重复数据时,我们通常采用Set来进行存储,而对于具体是使用HashSet,还是LinkedHashSet,或者TreeSet,我们需要更具需求来定,如果我们需要对存储后的数据进行按照要求排序输出时,我们一般使用TreeSet,没有这个要求时,我们一般常用HashSet,而LinkedHashSet通常只在可能有特殊需求(也就是需要输出的顺序和存储数据有关联时)才用到。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值