java笔记(十一):Collections

作为java集合框架的最后一部分,本节介绍下集合框架的内置功能以及实现这些功能的算法。
Collections类中的方法都是静态方法,用于完成某一特定的功能,比如排序,查找等。下面从源码层面介绍各种方法。
首先,需要对于一些名词进行解析。
由于存在存取的方式不同,list分为随机存取(RandomAccess)和迭代存取(Iterator),其实就是所谓的线性表和链表啦。
两者的区别也不用多说了,线性表是可以随时获取,但增删操作麻烦,链表相反。

sort

顾名思义,排序,不同的是可以传入Comparator参数用于自定义比较。

List arr = new ArrayList<String>();
arr.add("h");
arr.add("e");
arr.add("l");
arr.add("l");
arr.add("o");
Comparator c = new Comparator<String>() {
    public int compare(String o1, String o2) {
        return o2.compareTo(o1);
    }
};
Collections.sort(arr,c);
System.out.println(arr); //输出[o, l, l, h, e]]

该方法的排序本质还是传入的list内部的排序,sort()方法本身并未实现具体的排序功能。

binarySearch

二分查找,根据传入的list的种类不同,又分为随机存取的二分查找和迭代二分查找。
该方法需要传入的list是已经排好序的(升序),具体实现仍是传统的二分查找策略。
不同的是,获取mid值的方法:

int low = 0;
int high = list.size()-1;

while (low <= high) {
    int mid = (low + high) >>> 1;  //获取mid,采取移位
    Comparable<? super T> midVal = list.get(mid);
    int cmp = midVal.compareTo(key);

    if (cmp < 0)
        low = mid + 1;
    else if (cmp > 0)
        high = mid - 1;
    else
        return mid; // key found
}
return -(low + 1);  // key not found

reverse

同样分为随机存取的反转和迭代的反转。
线性的list反转的实现则是前后交换。

for (int i=0, mid=size>>1, j=size-1; i<mid; i++, j--)
    swap(list, i, j);

而迭代的list同样也是前后同时反转

ListIterator fwd = list.listIterator();
ListIterator rev = list.listIterator(size);
for (int i=0, mid=list.size()>>1; i<mid; i++) {
    Object tmp = fwd.next();
    fwd.set(rev.previous());
    rev.set(tmp);
}

shuffle

随机打乱list中个元素的位置。
其策略就是每次生成一个随机数,然后替换随机数那个位置的元素和当前元素。

swap

上述功能的实现都用到了交换功能,即交换两位指定位置的元素的位置。
List的元素交换的实现非常巧妙:

//交换l内i和j位置的元素
l.set(i, l.set(j, l.get(i)));

fill

填充功能,使用给定的元素填充指定的list

copy

将目标list复制到源list中
src.size()> dest.size()则报错
src.size()< dest.size()则剩下的元素不复制

min,max

获取list中的最小值和最大值

rotate

该方法称为旋转,即整个list的平移,给定步长,则每个元素按照给定的步长后移。
同样分为随机存取的list后移和迭代式后移。
先讲迭代的旋转实现方法。
由于list不能随机存储,因此,我们常见的链表式旋转实现方法如下:

reverse(list.subList(0, mid));
reverse(list.subList(mid, size));
reverse(list);

即对于(1,2,3,4,5,6), distance=2.
反转方式如下(1,2,3,4,5,6)–>(4,3,2,1,5,6)–>(4,3,2,1,6,5)–>(5,6,1,2,3,4)
注意该种方法对于随机存取的list也是可行的,但由于是随机存取,采用这种方法并不是最优的。
对于随机存取的旋转,具体的实现方式如下:

for (int cycleStart = 0, nMoved = 0; nMoved != size; cycleStart++) {
    T displaced = list.get(cycleStart);
    int i = cycleStart;
    do {
        i += distance;
        if (i >= size)
            i -= size;
        displaced = list.set(i, displaced);
        nMoved ++;
    } while (i != cycleStart);
}

replaceAll

新值换旧值

indexOfSubList

查找target在source出现的最小位置。
该方法的实现也是通常的挨个元素比较法,没有太大创新。不同的是对于迭代式的list,当最后判断出source的当前位置开始不是target时,需要回退。

lastIndexOfSubList

查找target在source出现的最大位置。
实现方式和indexOfSubList相似,只是从后面往前查找。

好了上面就是Collections里的主要算法功能的实现。
作为补充,举例讲解下数组和list如何转换。

from array to list

//from List to Array
ArrayList<String> list=new ArrayList<String>();
String[] strings = new String[list.size()];
list.toArray(strings);

//from Array to List
String[] s = {"a","b","c"};
List list = Arrays.asList(s);

unmodifiable

处于安全性考虑,Collections提供了大量额外的非功能性方法,其中之一便是生成原Collection的不可修改视图。
即返回的Collection和原Collection在元素上保持一致,但不可修改。
该实现主要是通过重写add,remove等方法来实现的。即在可能修改的方法中直接抛出异常。

public boolean add(E e) {
    throw new UnsupportedOperationException();
}

synchronized

可能你在使用java的Collection集合时就听过过Collection是非线程安全的,但一般很少告诉你如何实现现成安全,于是就有了这个方法。
synchronized方法返回线程安全的原Collection。
该方法保证线程安全不出意外仍是通过synchronized关键字来实现的。
稍微详细讲解下这一类方法。
首先在Collections类中对于各种Collection都进行了重写。
但重写方式都一致,即在内部重定义一个Collection作为对原Collection的引用,然后再加一个Object类的互斥锁——mutex,用于保证线程安全。

static class SynchronizedCollection<E> implements Collection<E>, Serializable {
    private static final long serialVersionUID = 3053995032091335093L;

    final Collection<E> c;  // Backing Collection
    final Object mutex;     // Object on which to synchronize

    SynchronizedCollection(Collection<E> c) {
        this.c = Objects.requireNonNull(c);
        mutex = this;
    }

    SynchronizedCollection(Collection<E> c, Object mutex) {
        this.c = Objects.requireNonNull(c);
        this.mutex = Objects.requireNonNull(mutex);
    }

知道了这个,就明白如何实现线程安全,其实是使用了装饰器模式。
比如:

public int size() {
    synchronized (mutex) {return c.size();}
}

使用mutex 实现互斥,然后仍是调用原Collection相应的方法。
最后官方也说了,可以使用等效的如下方法保证线程同步:

Collection c = Collections.synchronizedCollection(myCollection);
//上下两个方法都可以实现现成同步
synchronized (c) {
    Iterator i = c.iterator(); // Must be in the synchronized block
    while (i.hasNext())
       foo(i.next());
}

注意,使用Collections.synchronizeXXX返回的Collection和原Collection共用一套数据,并未实现数据的备份。

Checked

该系列方法保证增删的数据都是同类型的。

Empty

该系列方法保证返回空的Collection。

Singleton

保证生成的Collection只包含一个元素。

frequency

统计某个元素在Collection中出现的频率

disjoint

Returns true if the two specified collections have no elements in common. 不翻译了

addAll

Adds all of the specified elements to the specified collection
注意方法的定义:

public static <T> boolean addAll(Collection<? super T> c, T... elements) {
    boolean result = false;
    for (T element : elements)
        result |= c.add(element);
    return result;
}

到此,Collections类就浏览完毕。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值