黑马程序员-JAVA.Collections-基本集合使用

——Java培训、Android培训、iOS培训、.Net培训、期待与您交流! ——-


Java集合简介

集合结构类型在java设计之初就已经做了一些实现,但是由于需要满足更多需求,在java1.2中良好设计的Java Collections Framework(JCF) 出现了,带来了更优的体系结构和性能更高的实现。在java1.5泛型提出之后,整个Java Collections Framework按泛型架构重写了,至此,java的集合框架已具有相当好的可用度。

今天,首先大致了解一下JCF的体系结构,然后按接口逐个了解每个常用集合类的特点。
以下实例均以java1.8实现运行。

Java Collections Framework 大致接口层次:

Iterable : Iterable接口提供了for : 简便遍历的支持

  • Collections
    • List
    • Set
      • SortedSet
    • Queue
      • Deque
  • Map
    • SortedMap

Java Collections Framework 的常见集合:

容器接口抽象类常用实现其他实现
List<E>AbstractList<E>ArrayList<E>LinkedList<E>,Vector<E>,Stack<E>
Set<E>AbstractSet<E>HashSet<E>TreeSet<E>
Map<K,V>AbstractMap<K,V>HashMap<K,V>TreeMap<K,V>,Hashtable<K,V>,Properties

JCF之前的集合类

Vector,Stack,Hashtable,Properties 在JCF出现之前,这4位元老就已经服务了2个java版本了,而JCF一出现,也把他们加入了JCF体系,与1.2才出现的新集合类不同的是,他们的实现默认是线程同步的,故相对效率较低。


Collection接口:

Collection是元素容器的抽象,JCF中没有直接实现Collection接口的类
Collection接口成员
Collection接口只定义了不多的方法:
添加元素的boolean add(E e)boolean addAll(Collection<? extends E> c)
清空容器的void clear()
移除元素的boolean remove(Object o)boolean removeAll(Collection<?> c)boolean removeIf(Predicate<? super E> filter)boolean retainAll(Collection<?> c)(类似于交集运算)
判断元素存在的boolean contains(Object o)boolean containsAll(Collection<?> c)
判断容器是否为空的boolean isEmpty()
查询容器元素数量的int size()
提供迭代器的 Iterator<E> iterator()
转为数组的Object[] toArray()<T> T[] toArray(T[] a)
以及1.8新加入的流操作Stream<E> stream(),Stream<E> parallelStream(),Spliterator<E> spliterator()

List接口

List是元素列表的抽象,比Collection特殊的是,列表是有序的
List接口成员
List基于特性添加了以下方法:
在指定位置插入元素void add(int index, E element)
在指定位置插入集合addAll(int index, Collection<? extends E> c)
取得指定位置的元素E get(int index)
设置指定位置的元素E set(int index, E element)
移除指定位置的元素E remove(int index)
查找元素在列表中的位置int indexOf(Object o)(从头部开始,不存在返回-1),
int lastIndexOf(Object o)(从尾部开始)
提供特有的列表迭代器 ListIterator<E> listIterator()ListIterator<E> listIterator(int index)(从某个位置开始)
指定头尾的子列表 List<E> subList(int fromIndex, int toIndex)(含头不含尾)
流操作:排序void sort(Comparator<? super E> c),替换replaceAll(UnaryOperator<E> operator)

ArrayList数组列表

ArrayList是列表的数组实现,其内部结构是数组,特点是查询快O(1),增删效率低O(~n/2)+可能的数组重建操作。

LinkedList链表

LinkedList是列表的链接列表实现,其内部结构是链接节点(Node)组成的链,特点是增删效率高O(1),查询效率稍低O(~n/2)

Vector矢(这个译法不好)

Vector本意是向量,在N维空间中表示一个矢量需要N个值,所以Vector也就成了顺序数据结构的名称,它类似于ArrayList,是数组实现,但是同步的。

List的简单测试

构造长度为100000的三种List,分别测试添加,修改,插入,删除
结果如下:单位纳秒

ArrayList add cost:     12995692
LinkedList add cost:    11672940
Vector add cost:        8200942
ArrayList set cost:     7496400
LinkedList set cost:    6867191702
Vector set cost:        19437996
ArrayList Insert cost:  1243475741
LinkedList Insert cost: 20157028
Vector Insert cost:     1311598985
ArrayList Remove cost:  2497509350
LinkedList Remove cost: 20006097
Vector Remove cost:     2410710609

可见,在添加大量数据时,Vector最快,这是因为Vector的数组调整是倍增的,而ArrayList是50%增加,链表是线性增加
在查询修改数据时,链表的速度比另外两种慢2个数量级以上,这就是数据结构不同造成的差异
但是在插入和删除时,链表又表现出超过大约2个数量级的速度,同样也是因为数据结构不同
可见在实际使用中应该按需求选择容器。

Set接口

Set是元素集合的抽象,集合的特征是没有重复,Set也一样,它实现了容器内元素唯一性的机制。
Set的方法完全参照Collection,没有增加任何方法

HashSet哈希集合

HashSet是集合的哈希表实现,其内部结构是哈希表,其效率有赖于hashCode()方法的设计,良好的设计可以使查询和修改的效率都接近O(1)。

TreeSet二叉树集合

TreeSet实现了SortedSet,所以这是一个有序集合,依赖于元素自身实现Comparable自然排序或者外部比较器Comparator,如果都不存在将出现异常。其内部结构是二叉树,查询和修改效率与内容有关,在O(log(n))-O(n)之间。

Set的简单使用:
HashSet

HashSet必须依赖于hashCode以及equals方法,当然Object有默认实现:
TreeSet必须依赖于Comparable或者Comparator:


        final int count=10;
        //HashSet
        Set<UserC> hs=new HashSet<UserC>();
        byte[] rs=new byte[10];
        Random r=new Random();
        for (int i = 0; i < count; i++) {
            r.nextBytes(rs);
            hs.add(new UserC(new String(rs), r.nextLong(), r.nextBoolean()));
        }

        //test Unique
        sp(hs.size());
        for (UserC userC : hs) {
            hs.add(userC);
        }
        sp(hs.size());

        //TreeSet
        //without comparable
        Set<UserComparator> tsuc=new TreeSet<UserComparator>();
        /* the next without comparable || comparator causes  
         * java.lang.ClassCastException cannot be cast to java.lang.Comparable */
//      tsuc.add(new UserComparator());

        //comparable
        Set<UserC> tsu=new TreeSet<UserC>();
        for (int i = 0; i < count; i++) {
            r.nextBytes(rs);
            tsu.add(new UserC(new String(rs), r.nextLong(), r.nextBoolean()));
        }
        //sorted by birth
        for (UserC userC : tsu) {
            sp(userC);
        }
        sp();

        //comparator
        Set<UserC> tsu2=new TreeSet<UserC>(new UserComparator());
        for (int i = 0; i < count; i++) {
            r.nextBytes(rs);
            tsu2.add(new UserC(new String(rs), r.nextLong(), r.nextBoolean()));
        }
        //sorted by name
        for (UserC userC : tsu2) {
            sp(userC);
        }

Map接口

Map是映射容器的抽象,其包含的不是单个元素而是键值对(Entry)。
Map接口成员

Entry

Entry记录一个键值对。

实现:

HashMap

HashMap是映射容器的哈希表实现,其内部结构是哈希表,其效率有赖于hashCode()方法的设计,良好的设计可以使查询和修改的效率都接近O(1)。

Hashtable

Hashtable类似于HashMap,同样是哈希表实现,但是是JCF之前出现的,线程同步的容器。

Map的简单使用

HashMap类似于HashSet,依赖于hashCode以及equals方法,键和值中仅键参与哈希。


        final int count=10;
        //HashMap
        Map<UserC,Integer> hs=new HashMap<UserC,Integer>();
        byte[] rs=new byte[10];
        Random r=new Random();
        for (int i = 0; i < count; i++) {
            r.nextBytes(rs);
            hs.put(new UserC(new String(rs), r.nextLong(), r.nextBoolean()),r.nextInt(1000));
        }

        //test modify
        for (Entry<UserC, Integer> e : hs.entrySet()) {
            sp(e.getKey());
            sp(e.getValue());
        }
        for (UserC userC : hs.keySet()) {
            Integer i=hs.put(userC,-100);
            sp(i);
        }
        for (Entry<UserC, Integer> e : hs.entrySet()) {
            sp(e.getKey());
            sp(e.getValue());
        }

参与JCF的其他接口和特定方法

Iterator

Iterator是Collection的迭代器,它代表一个能遍历所属容器的工具,需要注意的是Iterator并没有一个当前元素,指针在两个元素之间:
Iterator接口成员
方法很简单:
是否能迭代到下一个元素:boolean hasNext()
下一个 :E next()
流操作:void remove()void forEachRemaining(Consumer<? super E> action)

ListIterator

ListIterator是List的特有迭代器,由于列表索引index存在所以功能更强大:
ListIterator接口成员
相比Iterator添加了方法:E next()
是否能迭代到前一个元素:boolean hasPrevious()
前一个:E previous()
下一个的列表索引:int nextIndex()
前一个的列表索引:int previousIndex()
设定当前元素:void set(E e)
在下一个元素之前添加元素:void add(E e)

Comparator

Comparator是某类型元素的外部比较器,它能为各种集合比较和排序提供支持。各种有序集合和排序方法可以利用这个进行排序。
Comparator接口成员
看似很多方法,实际常用的只有2个:
元素对比:int compare(T o1, T o2)
元素是否相等:boolean equals(Object obj)
此外还有流处理方法:
逆向比较器:Comparator<T> reversed()
词典式先后比较器系列方法:Comparator<T> thenComparing
以及一些静态方法:
自然顺序比较器:<T extends Comparable<? super T>> Comparator<T> naturalOrder()
逆向比较器:<T extends Comparable<? super T>> Comparator<T> reverseOrder()
null前置的允许null比较器:<T> Comparator<T> nullsFirst(Comparator<? super T> comparator)
null后置的允许null比较器:<T> Comparator<T> nullsLast(Comparator<? super T> comparator)
内部成员比较器系列方法:Comparator<T> comparing

Comparable

Comparable是针对元素的自然排序接口,实现它的元素能进行自然排序和比较。各种有序集合和排序方法可以利用这个进行自然排序。
Comparable接口成员
方法只有一个:
与另一个元素比较 public int compareTo(T o)

int hashCode()boolean equals(Object obj)方法

这两个方法覆盖了Object基类中的方法,目的是为元素提供哈希算法支持,Object默认实现是hashCode返回地址,equals比较引用对象是否是同一个。在许多情况下我们需要比较元素实际内容,实现更有效的哈希算法,这样就必须覆盖重写这两个方法。覆盖时有两个原则:1.equals返回true的对象哈希值必须相等;2.哈希值应该体现元素的状态,减少碰撞,均匀散布。


JCF中的工具类

Collections

这个工具类包装了大量用于操作集合的静态方法,包括诸如:排序,逆序,二分查找,置换,随机移位,填充容器,封装线程同步容器,封装不可修改的容器,单例容器等等,预置了相当多的方法和内部类。值得随时查阅调用。

Arrays

这个工具类包装了大量用于操作数组的静态方法,类似Collections,其方法包括:排序,并行排序,并行累加,二分查找,填充,比较,拷贝,切割,转向流等。

流式操作

流式操作是java1.8的新特性,是函数式编程的体现,这种语法能将集合以流的方式进行串联操作,可以有多个中间步骤和一个最终步骤。能简单的完成一系列复杂操作而且语法很有可读性。

例子:


        List<String> stringCollection = new ArrayList<>();
        stringCollection.add("d432a43dd2");
        stringCollection.add("aadfaa2");
        stringCollection.add("bb3a2b1");
        stringCollection.add("a1aa1");
        stringCollection.add("b2w3343bb3");
        stringCollection.add("cwcc");
        stringCollection.add("bbafsb2");
        stringCollection.add("qdd1");

        //demo1
        stringCollection.stream()
        .filter((s) -> s.length()>4)//filter
        .sorted()//sort
        .forEach(cc.sisel.util.Quic::sp);

        //demo2
        Optional<String> result=
                stringCollection.stream()
        .map(String::toUpperCase)//toUpper
        .filter((s) -> s.contains("A"))//collect A
        .limit(8)//length<=8
        .reduce((s1,s2) -> s1+"#@#"+s2);//merge
        sp(result.get());

List测试代码:


        final int count =100000;
        List<Integer> larr=new ArrayList<>(),
                llin=new LinkedList<>(),lv=new Vector<>();
        int[] source=new int[count];
        ArrayList<Integer> rd=new ArrayList<Integer>(count);
        Random r=new Random();
        for (int i = 0; i < source.length; i++) {
            source[i]=r.nextInt(count);
            rd.add(i, i);
        }
        Collections.shuffle(rd);
        long before,after;
        //add test
        before=System.nanoTime();
        for (int i = 0; i < count; i++) {
            larr.add(source[i]);
        }
        after=System.nanoTime();
        sp("ArrayList add cost:\t"+(after-before));
        before=System.nanoTime();
        for (int i = 0; i < count; i++) {
            llin.add(source[i]);
        }
        after=System.nanoTime();
        sp("LinkedList add cost:\t"+(after-before));
        before=System.nanoTime();
        for (int i = 0; i < count; i++) {
            lv.add(source[i]);
        }
        after=System.nanoTime();
        sp("Vector add cost:\t"+(after-before));

        //set test
        before=System.nanoTime();
        for (int i = 0; i < count; i++) {
            larr.set(rd.get(i), -1);
        }
        after=System.nanoTime();
        sp("ArrayList set cost:\t"+(after-before));
        before=System.nanoTime();
        for (int i = 0; i < count; i++) {
            llin.set(rd.get(i), -1);
        }
        after=System.nanoTime();
        sp("LinkedList set cost:\t"+(after-before));
        before=System.nanoTime();
        for (int i = 0; i < count; i++) {
            lv.set(rd.get(i), -1);
        }
        after=System.nanoTime();
        sp("Vector set cost:\t"+(after-before));

        //insert test
        before=System.nanoTime();
        for (ListIterator<Integer> iterator = larr.listIterator(); iterator.hasNext();) {
            if(iterator.hasNext()){
                iterator.add(-99);
            }
            iterator.next();
        }
        after=System.nanoTime();
        sp("ArrayList Insert cost:\t"+(after-before));
        before=System.nanoTime();
        for (ListIterator<Integer> iterator = llin.listIterator(); iterator.hasNext();) {
            if(iterator.hasNext()){
                iterator.add(-99);
            }
            iterator.next();
        }
        after=System.nanoTime();
        sp("LinkedList Insert cost:\t"+(after-before));
        for (ListIterator<Integer> iterator = lv.listIterator(); iterator.hasNext();) {
            if(iterator.hasNext()){
                iterator.add(-99);
            }
            iterator.next();
        }
        after=System.nanoTime();
        sp("Vector Insert cost:\t"+(after-before));

        //remove test
        before=System.nanoTime();
        for (ListIterator<Integer> iterator = larr.listIterator(larr.size()/2);larr.size()>0 ; ) {
            if(iterator.hasPrevious()){
                iterator.previous();
                iterator.remove();
            }
            if(iterator.hasNext()){
                iterator.next();
                iterator.remove();
            }
        }
        after=System.nanoTime();
        sp("ArrayList Remove cost:\t"+(after-before));
        before=System.nanoTime();
        for (ListIterator<Integer> iterator = llin.listIterator(llin.size()/2);llin.size()>0 ; ) {
            if(iterator.hasPrevious()){
                iterator.previous();
                iterator.remove();
            }
            if(iterator.hasNext()){
                iterator.next();
                iterator.remove();
            }
        }
        after=System.nanoTime();
        sp("LinkedList Remove cost:\t"+(after-before));
        for (ListIterator<Integer> iterator = lv.listIterator(lv.size()/2);lv.size()>0 ; ) {
            if(iterator.hasPrevious()){
                iterator.previous();
                iterator.remove();
            }
            if(iterator.hasNext()){
                iterator.next();
                iterator.remove();
            }
        }
        after=System.nanoTime();
        sp("Vector Remove cost:\t"+(after-before));

User 类以及UserComparator


class UserC implements Comparable<UserC>{
    String name;
    long birth;
    boolean isMale;

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + (int) (birth ^ (birth >>> 32));
        result = prime * result + (isMale ? 1231 : 1237);
        result = prime * result + ((name == null) ? 0 : name.hashCode());
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        UserC other = (UserC) obj;
        if (birth != other.birth)
            return false;
        if (isMale != other.isMale)
            return false;
        if (name == null) {
            if (other.name != null)
                return false;
        } else if (!name.equals(other.name))
            return false;
        return true;
    }

    public UserC(String name, long birth, boolean isMale) {
        super();
        this.name = name;
        this.birth = birth;
        this.isMale = isMale;
    }

    @Override
    public String toString() {
        return "UserC [name=" + name + ", birth=" + birth + ", Male="
                + isMale + "]";
    }

    @Override
    public int compareTo(UserC o) {
        long span=this.birth-o.birth;
        if(span==0){
            int n =this.name.compareTo(o.name);
            if(n==0){
                if(this.isMale^o.isMale){
                    return this.isMale?1:-1;
                }
                return 0;
            }
            return n;
        }
        return (int) span;
    }

}

class UserComparator implements Comparator<UserC>{

    @Override
    public int compare(UserC o1, UserC o2) {
        int n =o1.name.compareTo(o2.name);
        if(n==0){
            long span=o1.birth-o2.birth;
            if(span==0){
                if(o1.isMale^o2.isMale){
                    return o1.isMale?1:-1;
                }
                return 0;
            }
            return (int) span;
        }
        return n;
    }

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值