集合框架总结

Collection

List和Set

在实际开发中,需要将使用的对象存储于特定数据结构的容器中。JDK提供了这样的容器——集合(Collection)。

Collection是一个接口,定义了集合相关的操作方法,其中两个子接口:List和Set

List:可重复集合

Set:不可重复集合

注:元素是否重复,取决于equals()比较结果。

集合持有对象的引用

集合中存储的都是引用类型元素,并且集合只保存每个元素对象的引用,而并非将元素对象本身存入集合。

 

add方法

1)Collection定义了一个add方法用于向集合中添加新元素。——boolean add(E e)

2)该方法会将给定的元素添加进集合,若添加成功则返回true,否则返回false。

 

Contains方法

1)boolean contains(Object o)

2)该方法会用于判断给定的元素是否包含在集合中,若包含则返回true,否则返回false

注:集合在判断元素是否被包含在集合中是根据每个元素的equals()方法进行比较后的结果。

       通常有必要重写equals()保证contains()方法的合理结果

 

size,clear,isEmpty

1)int size():该方法用于返回当前集合中的元素总和。

2)void clear():该方法用于清空当前集合。

3)boolean isEmpty():该方法判断当前集合中是否包不包含任何元素。

 

addAll和containsAll方法  (集合的批量操作)

1)boolean addAll(Collection<? extends E> c)

该方法需要我们传入一个集合,并将该集合中的所有元素添加到当前集合中。

如果此collection由于调用而发生改变则返回true。

2)boolean containsAll(Collection<? extends E> c)

该方法用于判断当前集合是否包含给定集合中的所有元素,若包含则返回true。

(1)判断当前集合是否包含给定集合的所有元素

(2)判断依据就是根据元素equals比较的

 

remove():删除集合元素

c.remove(p);

1)remove()方法会删除集合中与给定元素第一个

2)equals比较为true的元素。

 

Iterator[  hasNext,next方法  ]

迭代器用于遍历集合元素,获取迭代器可以使用collection定义的方法。

迭代器Iterator是一个接口。集合在重写collection的iterator()方法时利用内部类提供了迭代器的实现

 Iterator提供了统一的遍历集合元素的方式,其提供了用于遍历集合的两个 方法:

——boolean hasNext():判断集合是否还有元素可以遍历。

——E next():返回迭代的下一个元素

 

remove方法

 在使用迭代器遍历集合时,不能通过集合的remove方法删除集合元素,否则会抛出并发更改异常。我们可以

 通过迭代器自身提供的remove()方法来删除通过next方法迭代出的元素。

——void remove()

迭代器的删除方法是在原集合中删除元素。

这一需要注意的是,在调用remove方法前必须通过迭代器的next()方法迭代过元素,那么删除的就是这个元素

。并且不能再次调用remove方法,除非再次调用next()方法方可再次调用。

pubic void testRemove(){

      Collection<String> c=new HashSet<String>();
      c.add("java");
      c.add("C#");
      c.add("php");
      Iterator<String> it=c.iterator();
      while(it.hasNext()){
           String str=it.next();
           if(str.indexOf('c')!=-1){
               it.remove();
        }
    }

    System.out.println(c);
}

增强型for循环

java5.0后推出了一个新的特性,曾倩for循环,也称为新循环。该循环不通用于传统循环的工作,其只用于遍历集合和数组。

for(元素类型 e:集合或数组){

       循环体

}

注意:新循环并非新的语法,而是在编译过程中,编译器会将新循环转换为迭代器模式。所以新循环本质上是一个迭代器

 

泛型机制

泛型在集合中的应用

1)泛型是JavaSE 5.0引入的特性,泛型的本质是参数化类型。在类,接口和方法的定义过程中,所操作的数据类型被传入的参数指定。

2)Java泛型机制广泛的应用在集合框架中。所有的集合类型都带有泛型参数,这样在创建集合时可以指定

放入集合中元素的类型。Java编译器可以据此进行类型检查。这样可以减少代码在运行时出现错误的可能性。

 

集合操作——线性表

List

ArrayList和LinkList

1)List接口是Collection的子接口,用于定义线性表数据结构。可以将List理解为存放对象的数组,只不过其

元素个数可以动态的增加或减少。

2)List接口的两个常见实现类为ArrayList和LinkedList,分别用动态数组和链表的方式实现了List接口。

3)可以认为ArrayList和LinkedList的方法在逻辑上完全一样。只是在性能上有一定的差别。ArrayList更适合于随机访问而LinkedList更适合于插入和删除。在性能要求不是特别

严格的情形下可以忽略这个差别。

 get和set方法

1)List除了继承了Collection定义的方法外,还根据其线性表的数据结构定义了一系列方法,其中最常用的就是基于下标的get和set方法:

——E  get(int  index):获取集合中指定下标对应的元素,下标从0开始。

——E set(int  index,E element):将给定元素存入给定位置,并将原位置的元素返回。

插入和删除

List根据下标的操作还支持插入与删除操作。

——void add(int index,E element):将给定的元素插入到指定位置,原位置及后续元素都顺序向后移动。

——E remove(int  index):删除给定位置的元素,并将被删除的元素返回。

 

subList

list的subList方法用于获取子List

注意:subList获取的list与原List占有相同的存储空间,对子list操作会影响原List。

List<E> subList(int e1,int  e2):e1和e2是截取子List的首尾下标(前包括后不包括)

 

List转换为数组

List的toArray方法用于将集合转换为数组。但实际上该方法是在Collection中定义的,所以所有的集合都有这个功能。

其有两个方法:

Object[  ] toArray()

<T> T[  ] toArray(T[  ] a)

其中第二个方法是比较常用的,我们可以传入一个指定类型的数组,该数组的元素类型应与集合的元素类型一致。返回值则是转换后的数组,该数组会保存集合中所有的元素。

 

 

数组转换为List

Arrays类中提供了一个静态方法asList,使用该方法我们可以将一个数组转换为对应的List集合。

其方法定义为: static  <T>List<T> asList<T...a>

返回的List的集合元素类型由传入的数组的元素类型决定,并且要注意的是,返回的集合我们不能对其增删操作,否则会抛出异常。并且对集合的元素进行修改会影响数组对应的元素。

List排序

 Collections.sort方法实现排序

1)Collections是集合的工具类,它提供了很多便于我们操作集合的方法,其中就有用于集合排序sort方法。

该方法的定义为

——void  sort(List<T> list)

该方法的作用是对给定的集合元素进行自然排序。

public void testsort(){

 List<Integer>list=new ArrayList<Integer>();
 Random r=new Random(1);
 for(int i=0;i<10;i++){
          list.add(r.nextInt(100));
   }

   System.out.println(list);
    
    Collections.sort(list);

   System.out.println(list);
}

 

Comparable

Collections的sort方法时对集合元素进行自然排序,那么两个元素对象之间就一定要有大小之分。这个大小之分是如何界定的?实际上,在使用Collections的sort方法排序的集合元素都必须是Comparable接口的实现类,该接口表示其子类是可比较的,因为事先该接口必修重写抽象方法:

——int  compareTo(T  t);

该方法用于使当前对象与给定对象进行比较。

——若当前对象大于给定对象,那么返回值应为>0的整数。

——若小于给定对象,那么返回值应为<0的整数。

——若两个对象相等,则返回0.

public void testComparable(){

   /*
     cell实现了Comparable接口
     CompareTo方法逻辑为按照row值的大小排序

    public int compareTo(cell o){
         return this.row-o.row;
    }
*/

    List<cell> cells=new ArrayList<cell>();
    cells.add(new cell(2,3));
    cells.add(new cell(5,3));
    cells.add(new cell(4,2));
    cells.add(new cell(1,3));
    Collections.sort(cells);
}

 

 

Comparator

一旦Java实现了Comparable接口,其比较逻辑就已经确定;如果希望在排序的操作中临时指定比较规则,可以采用Comparator接口回调的方式。

——int  compare(T o1,T o2);

该方法的返回值要求:

——若o1>o2则返回值应>0;

——若返回值o1<o2则返回值应为<0

——若o1==o2则返回值应为0

public void testComparator(){

    List<cell> cells=new ArrayList<cell>();
    cells.add(new cell(2,3));
    cells.add(new cell(5,3));
    cells.add(new cell(4,2));
    cells.add(new cell(1,3));
    Collections.sort(cells);
  //按照col值的大小排序
  Collections.sort(cells,new Comparator<Cell>(){

        public int compare(Cell o1,Cell o2){
                 return o1.col-o2.col;
            }
     });
}

 

 

队列和栈

Queue

1)队列是常用的数据结构,可以将队列看成特殊的线性表,队列限制了线性表的访问方式:只能从线性表的一端添加(offer)元素,从另一端取出(poll)元素。

2)队列遵循先进先出的原则。

3)JDK提供了Queue接口,同时使得LinkedList实现了该接口(选择LinkedList实现Queue的原因在于Queue经常要进行添加和删除的操作,二LinkedList在这方面效率较高)

Queue接口中主要方法如下:

boolean offer(E e):将一个对象添加至队尾,如果添加成功则返回true。

E poll():从队首删除并返回一个元素。

E peek();返回队首元素(但并不删除)。

public void testQueue(){

    Queue<String> queue=new LinkedList<String>();
    queue.offer("a");
    queue.offer("b");
    queue.offer("c");

    System.out.println(queue); //[a,b,c]
    String str=queue.peek();
     System.out.println(str); //a
     while(queue.size()>0){

      str=queue.poll();
      System.out.println(str);//a,b,c
  }
}

 

Deque

1)Deque是Queue的子接口,定义了所谓“双端队列”即从队列的两端分别可以入队(offer)和出队(poll),LinkedList实现了该接口。

如果将Deque限制为只能从高一段入队和出队,则可实现“栈”(stack)的数据结构,对于栈而言,入栈称为push,出栈称之为pop.

2)栈遵循先进后出原则。

public void testStatck(){


      Deque<String> stack=new LinkedList<String>();
      stack.push("a");
      stack.push("b");
      stack.push("c");
      System.out.println(stack);//[c,b,a]
      String str=stack.peek();
      System.out.println(str);//c
      while(stack.size()>0){
          str=stack.pop();
          System.out.print(str+"  ");//c,b,a
  }
}

 

 

 

 

Map接口

1)Map接口定义的集合又称查找表,用于存储所谓“key-value”映射对。key可以看成是value的索引,作为key的对象在集合中不可以重复。

2)根据内部数据结构的不同,Map接口有多种实现类,其中常用的有内部为hash表实现的HashMap和内部为二叉树实现的TreeMap。

put()方法

1)Map接口中定义了向Map中存放元素的put方法:——V put(K key,V value)

2)将key-value对存入Map,如果在集合中已经包含该key,则操作将替换该key所对应的value,返回值为该key所对应的value(如果没有则返回null)

 

get()方法

1)Map接口中定义了从Map中获取元素的get方法:

——V get(Object key)

2)返回参数key所对应的value对象,如果不存在则返回null。

 

containsKey()方法

1)Map接口中定义了判断某个key是否在Map中存在:

——boolean containsKey(Object key);

若map中包含给定的key则返回true,否则返回false。

 

HashMap

1)从HashMap的原理中我们可以看到,key的hashCode()方法 的返回值对HashMap存储元素是会起着很重要的作用。

而hashCode()方法实际上是Object中定义的那么应当妥善重写该方法:

对于重写了equals方法的对象,一般要妥善的重写继承自Object类的hashCode方法(Object提供的hashCode方法将返回该对象所在的内存地址的整数形式)。

2)重写hashCode方法时需要注意两点:其一,与equals方法的一致性,即equals比较返回true的两个对象其hashCode方法返回值应当相同;其二,hashCode方法的数值应符合hash算法的要求,试想如果有很对对hashCode方法返回值都相同,则会大大降低hash表的效率,一般情况下可以使用IDE提供的自动生成的hashCode方法。

 

装载因子及HashMap优化

1)Capacity:容量,hash表里bucket(桶)的数量,也就是三列数组的大小

2)Initial capacity:初始容量,创建hash表时,初始bucket的数量,默认构建容量是16,也可以使用特定容量。

3)Size:大小,当前散列表中存储数据的数量。

4)Load factor:加载因子,默认值为0.75,当向散列表增加数据时如果size/capacity的值大于Loadfactor则发生扩容并且重新散列(rehash)。

5)性能优化:加载因子较小时,散列查询性能会提高,同时  也浪费了散列桶空间容量。0.75是性能和空间相对平衡结果,在创建散列表时指定合理容量,减少rehash提高性能

 

Map的遍历

Map提供了三种遍历方式:

——遍历所有的key

——遍历所有的key-value对

——遍历所有的value(不常用)

 

遍历所有 key的方法

——Set<k> keySet()

——该方法会将当前Map中所有的key存入一个Set集合后返回。

public void testKeySet(){

    Set<String> keySet=map.keySet();
    for(String key:keySet){
       System.out.println(key);
  }
}

 

 

使用entrySet()方法遍历所有键值对

——Set<Entry<K,V>> entrySet()

该方法会将当前Map中每一组key-value对封装为一个Entry对象并存入一个Set集合后返回。

 

LinkedHashMap实现有序Map

1)使用Map接口的哈希表和链表实现,具有可预知的迭代顺序。次实现与HashMap的不同之处在于:

LinkedHashMap维护着一个双向循环链表。次链表定义了迭代顺序,该迭代顺序通常就是存放元素的顺序。

2)如果在Map中重新存入已有的key。那么key的位置不会发生改变,只是将value值替换。

 

posted on 2017-08-30 20:59  左夕  阅读(151)  评论(0)    收藏  举报

导航