Java 集合遍历探索

Java集合遍历探索

  • Iterable
Iterable:顾名思义,实现了这个接口的对象支持迭代,是可迭代的。
  • Iterator
同样,这是迭代器,它就是提供迭代机制的对象,具体如何迭代,都有Iterator规
范的迭代器是一种模式,它能够使序列类型的数据结构的遍历行为与被便利的对
象本省分离,即当我们遍历时不需要关心数据结构的具体底层结构。只要拿到这
个对象,使用迭代器便可以对这个对象进行遍历。
  • list的for each遍历默认底层还是为迭代器遍历
#  代码如:
    public  static void foreachIteratorList(){
        List<Integer> integerList = new ArrayList<Integer>();
        integerList.add(1);
        integerList.add(2);
        integerList.add(3);
        integerList.add(4);

        if (CollectionUtils.isNotEmpty(integerList)){
            for (int num : integerList){
                System.out.println(num);
            }
        }
    }

# 编译后的字节码:
  public static void foreachIteratorList()
  {
    List integerList = new ArrayList();
    integerList.add(Integer.valueOf(1));
    integerList.add(Integer.valueOf(2));
    integerList.add(Integer.valueOf(3));
    integerList.add(Integer.valueOf(4));
    Iterator localIterator;
    if (CollectionUtils.isNotEmpty(integerList))
      for (localIterator = integerList.iterator(); localIterator.hasNext(); ) { 
      		int num = ((Integer)localIterator.next()).intValue();
        	System.out.println(num);
      }
  }

上述可发现对于类型不为Object的元素,默认会进行强转,这是因为编译器默认的泛型擦除
导致的

# 修改过后的代码:
public  static void forIteratorList(){
    
    List<Integer> integerList = new ArrayList<Integer>();
    integerList.add(1);
    integerList.add(2);
    integerList.add(3);
    integerList.add(4);

    Iterator<Integer> iterator ;
    if (CollectionUtils.isNotEmpty(integerList)){
        for ( iterator = integerList.iterator();iterator.hasNext();){
            System.out.println(iterator.next());
        }
    }
}

## 对于上述ArrayList迭代器方式遍历,默认ArrayList的源码实现为 Itr

private class Itr implements Iterator<E> {
    // 用于返回下个元素的索引,即可理解为元素下标索引值
    int cursor;      
    // 返回最新调用的下一个元素或者前一个元素的索引,如果不存在
    // 则返回 -1
    int lastRet = -1;
    
    /** 计数器,用于检测集合是否存在并发修改
    * AbstractList中定义了 protected transient int modCount = 0;
    * 每当对集合进行数据结构变更,该值都会加1,如add(),remove()等方
    * 法
    int expectedModCount = modCount;

    // 若当前游标的位置与集合的大小相等,则返回false表示没有下个元素
    public boolean hasNext() {
        return cursor != size;
    }

    // 返回下一个元素
    @SuppressWarnings("unchecked")
    public E next() {
        // 检查是否存在并发操作
        checkForComodification();
        int i = cursor;
        if (i >= size)
            throw new NoSuchElementException();
        // 把当前集合的元素重新赋值给数组
        Object[] elementData = ArrayList.this.elementData;
        if (i >= elementData.length)
            throw new ConcurrentModificationException();
        cursor = i + 1;
        // 返回当前元素
        return (E) elementData[lastRet = i];
    }

    // 从集合中移除元素
    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();
        }
    }

    @Override
    @SuppressWarnings("unchecked")
    public void forEachRemaining(Consumer<? super E> consumer) {
        Objects.requireNonNull(consumer);
        final int size = ArrayList.this.size;
        int i = cursor;
        if (i >= size) {
            return;
        }
        final Object[] elementData = ArrayList.this.elementData;
        if (i >= elementData.length) {
            throw new ConcurrentModificationException();
        }
        while (i != size && modCount == expectedModCount) {
            consumer.accept((E) elementData[i++]);
        }
        // update once at end of iteration to reduce heap write traffic
        cursor = i;
        lastRet = i - 1;
        checkForComodification();
    }

    // 检查是否存在并发操作
    final void checkForComodification() {
        if (modCount != expectedModCount)
            throw new ConcurrentModificationException();
    }
}

由上可知,for each遍历,底层采用的是iterator迭代器方式进行遍历的,若遇到大数据量,可以直接采用iterator遍历,避免因编译时的转换而消耗时间

Java Collection

  • Collections 介绍
collection ---有时被人称为容器,只是将多个元素分组为单个单元的对象。Collections 主要
用来存储,检索,操作,和通信聚合等,类似于一个扑克牌盒,信箱。
  • 什么是集合框架?
一种集合框架是用来代表和操作集合的统一的框架。一个集合框架包含以下特性:

1.Interfaces(接口):这些是用来代表集合的抽象数据类型。Interfaces允许被它们代表
的集合详细进行独立的操作,在面向对象语言中,interfaces generally form a
hierarchy。

2.Implementations(实现):这些是集合的具体实现,本质上,它们是可重用的数据结构

3.Algorithms (算法): 这些主要用于实现接口对象中的排序、搜索等有效的算法,不同的
接口实现方法可以使用同一种算法,总之,算法是可重用的方法。

  • Java 集合框架的好处?
1.减少编写多余的程序
2.提高程序的速度和质量
3.允许不相关的API互相协调工作
4.减少学习和使用新的API的工作
5.减少设计新的API
6.提高软件复用性
  • Interfaces
这些核心接口封装了不同类型的集合,核心接口是Java集合框架的基础,允许你
根据不同类型接口独立地操作,如下:

       Collection                         Map
          |                                |
 ---------------------                     |
 |     |     |       |                     | 
Set   List  Queue  Deque               SortedMap

Set 是一种特殊的Collection,SortedSet 是特殊的Set,如上该继承关系包含两
颗树,Map 并不是真正的Collection

注意 核心集合所有的接口都是通用的,如Collection接口:

public interface Collection<E>...

其中<E>语法告诉你该接口是通用的(泛型),当你声明一个接口实例的时候应该声
明接口中包含元素对象的类型,使编译器可以在运行时进行类型检查,从而减少运
行时错误。

Collection:集合的根接口,该接口是最少的和公共通用的,所有的接口实现它来
扩展和管理自己想要的接口。一些是有序或无序的,有的允许重复的元素等。

Set:不能包含重复元素的接口

List:一个有序的集合(有时也称为序列),该集合可以包含重复的元素,通常使用
List的用户可以更加准确的控制和操作通过下标访问的集合中每一个感兴趣的元
素,如果你使用过Vector,那么你应该会更加喜欢List

Queue:一个用于管理和处理多个元素的优先级的集合,除了Collection的基本操
作外,一个Queue应该提供一些额外的 插入、取出、检索等操作。

Queue 很典型但非是必须的,管理的元素是有序的且FIFO(First in First Out
),根据提供的元素的自然顺序比较的队列是比较特殊的优先级队列。无论采用什么
排序方式,队列头部的元素将会被调用 remove或 poll方法移除掉。在FIFO队列中,
新的队列元素总是会被插入到队尾,其它队列可能采用不同的添加元素规则,每个队
列的实现都必须指明它的排序属性

Deque:一个用于处理多个元素优先级的队列。除Collection的基本操作,一个Deque
应该提供额外的插入、取出、检索等操作。

Deque既可以使用FIFO也可以使用LIFO(Last In ,First Out),在一个deque中所有
的新元素都可以从两端插入、取出、删除。

Map:是一个keys和values的对象,一个Map不可以包含重复的keys,每个key可以匹配
最多一个值。如果你熟悉基本的 Map,你应该使用过 Hashtable 

Set和Map最后两个核心的排序版本的集合接口:

SortedSet: Set 包含它以升序排序的元素,提供一些利用该排序的额外操作,
SortedSet用于一些自然排序的集合,如单词列表和成绩单

SortedMap: 一个key为升序的Map,该集合类似于SortedSet,SortedMap主要用于
一些key/value自然排序的集合,如字典、电话簿

  • Set 接口
Set 接口是一个不能包含重复元素的集合,它对数学集合的抽象建模,该接口只包含
了从Collection继承的方法,而且禁止了添加重复的元素。Set也添加了一些强的协
议equals和hashcode操作,允许Set的实例可以被比较,尽管他们不同类型的实现,
两个Set实例如果包含相同的元素,则它们相等。

Java平台包含了三种目的的实现:HashSet 、TreeSet、LinkedHashSet

HashSet:它用一张哈希表存储它的元素,是最好性能的实现,但是它无法保证遍历的
顺序。

TreeSet:它用红黑树存储它的元素,根据元素值进行排序,性能比HashSet略微差点

LinkedHashSet:它用一个哈希表和链表实现,根据元素插入Set时的顺序进行排序,
通常通过HashSet以略高的代价来保证顺序
  • List 接口
 List 是有序的集合(有时也称为序列),List可以包含重复的元素,除了从
 Collection继承的操作外,还有一些额外的操作,如下:

Positional access(位置访问):对元素的操作基于它们在List中的数字位置,
这个主要包括方法:get、set、add、addAll、remove等

Search(查询):查找一个在List中指定对象的位置,返回它的下标位置,主要方
法为:indexOf、lastIndexOf

Iteration(遍历) :继承于Iterator,充分利用List的自然排序,该 listIterator
方法提供了这个行为

Range-view(局部视图):sublist 方法实现了在List中任意范围的操作

Java平台包含了两个通用的List实现,ArrayList(最好性能的实现)和
LinkedList(特定情况下最好性能的实现)
  • Range-View Operation 范围视图操作
该 Range-View Operation ,subList(int fromIndex,int toIndex),返回这个
list指定下标范围内的视图list,包含fromIndex,不包含toIndex

从list尾部删除元素比首部开始删除元素性能好
  • Queue的实现
Queue的实现通常不允许插入null元素, LinkedList 为重新设计的Queue的实现,
由于历史原因,它为特例,允许插入null元素,但也可以利用该特点,通过poll和
peek方法允许返回null 
  • Deque
Deque 全称为 double-ended-queue,是一个线性集合,支持两端插入和删除元素
该接口是一个丰富的抽象数据类型接口比Stack和Queue,因为Deque同时实现了Stack
和Queue,该接口定义了访问Deque实例两端访问元素的方法,也提供了insert、remove
examine,之前实现的类如: ArrayDeque 和  LinkedList 

Insert :
 addfirst 和 offerFirst方法在Deque实例首部插入元素,addLast 和 offerLast
 在Deque实例尾部插入元素,当Deque的容量达到限制时,offerFirst 和offerLast
 方法更加合适,因为如果队列满时会抛出异常,添加失败

Remove:
removeFirst 和 pollFirst 方法从Deque实例首部移除元素,removeLast 和
 pollLast 方法从尾部移除元素,如果Deque为空,pollFirst 和 pollLast 方法
 返回null,removeFirst 和 removeLast会抛出异常

 Retrieve:
 getFirst and peekFirst 获取Deque实例首部的第一个元素,这些方法不会从队列
 删除值, getLast 和 peekLast获取最后一个元素,若Deque队列为空,则方法
 getFirst and getLast 会抛出异常,peekFirst and peekLast 返回null

 除上述方法外,还有一些额外的预先定义的方法,如removeFirstOccurence 删除
 第一次出现的指定元素,若存在则删除,不存在则无用,类似removeLastOccurence
  • Map
对于Map中的桶操作 (containsAll, removeAll, and retainAll)是非常有用的工
具方法,以下操作前提为你知道哪个结合是子集合,以下假设第一个集合包含第二个
集合
// 判断是否包含
if (m1.entrySet().containsAll(m2.entrySet())) {
    ...
}
// 或者可以判断key
if (m1.keySet().equals(m2.keySet())) {
    ...
}
// 去除不需要的
static <K, V> boolean validate(Map<K, V> attrMap, 
			 Set<K> requiredAttrs, Set<K>permittedAttrs) {
    boolean valid = true;
    Set<K> attrs = attrMap.keySet();

    if (! attrs.containsAll(requiredAttrs)) {
        Set<K> missing = new HashSet<K>(requiredAttrs);
        missing.removeAll(attrs);
        System.out.println("Missing attributes: " + missing);
        valid = false;
    }
    if (! permittedAttrs.containsAll(attrs)) {
        Set<K> illegal = new HashSet<K>(attrs);
        illegal.removeAll(permittedAttrs);
        System.out.println("Illegal attributes: " + illegal);
        valid = false;
    }
    return valid;
}
// 获取两个集合中共同的元素
Set<KeyType>commonKeys = new HashSet<KeyType>(m1.keySet());
commonKeys.retainAll(m2.keySet());

//去除包含指定集合中的元素
m1.entrySet().removeAll(m2.entrySet());

// 查找指定文件中相同的单词(字母相同,顺序不同)
Map<String,List<String>> m = new HashMap<String, List<String>>();
try {
    Scanner s = new Scanner(new File("F://a.txt"));
    while (s.hasNext()){
        String world = s.next();
        String alpha = alphabetize(world);
        // 此出为map的良好用法
        List<String> list = m.get(alpha);
        if (list == null)
            m.put(alpha,list = new ArrayList<String>());
        list.add(alpha);
    }
    // 打印出 处理后的结果
    int minGroupSize = 2;
    for (List<String> l :m.values()){
        if (l.size() >= minGroupSize){
            System.out.println("集合大小:"+l.size()+":"+l);
        }
    }
} catch (FileNotFoundException e) {
    e.printStackTrace();
}
}
// 把字母相同顺序不同的单词全部转换为同一个单词
// 如:abc,acb,cab 全部会转换为 abc
private static String alphabetize(String world) {
  char[] a = world.toCharArray();
  Arrays.sort(a);
  return new String(a);
}
  • Object Ordering
List 1 排序如下:
Collections.sort(l);
如果List包含的为String,它将会按照字母表顺序排序,如果为Date元素,它将会
按照时间的先后顺序排序

排序是如何实现的呢?
String和Date都实现了 Comparable接口,该接口提供了对元素的自然排序的实现
类,允许对象能够自动排序,以下为Java平台中重要的一些实现了该接口的类
Classes Implementing Comparable

Class	Natural Ordering
Byte	Signed numerical
Character	Unsigned numerical
Long	Signed numerical
Integer	Signed numerical
Short	Signed numerical
Double	Signed numerical
Float	Signed numerical
BigInteger	Signed numerical
BigDecimal	Signed numerical
Boolean	Boolean.FALSE < Boolean.TRUE
File	System-dependent lexicographic on path name
String	Lexicographic
Date	Chronological
CollationKey	Locale-specific lexicographic

如果你要对List中存放的元素排序,但是他们没有实现Comparable接口,
 Collections.sort(list)则会抛出 ClassCastException

  • Comparable 接口
该接口的唯一方法:
public interface Comparable<T> {
    public int compareTo(T o);
}

该方法返回一个正数或负数值,取决于,当前对象是否小于、等于、大于接受的对象
,若接受的对象不能够进行比较则会抛出异常 ClassCastException.

实现自己的Compare方法,实现该Comparable接口,重写equals和hashcode方法
  • Comparators接口
//如果不想以自然顺序比较或者对一些没有实现Comparable对象排序,则应该实现
//  Comparator 该接口提供的方法
public interface Comparator<T> {
    int compare(T o1, T o2);
}
该方法会比较它的两个参数,返回负数、0、正数取决于第一个参数小于、等于、大
于第二个参数,若传入非法参数类型,则会抛出ClassCastException

猜你喜欢

转载自blog.csdn.net/yaoqiancuo3276/article/details/86699133