Java开发(二) 常用接口解析——Collection

如果你是一个初学者,相信看到Java Documentation 是懵逼的,说实话这点还是比较想吐槽的,如果接触的教材不好,有一些接口和包可能即使做了课程设计也还是懵的。

想来这也是博客存在的意义,教材本应因人而异,但这又办不到,博客虽不能当教材,但也权当补充,毕竟很多人就是想找一句点通问题的话。所以感谢《Data Structure and Algorithm Analysis in Java》这本书,紧接上次谈的泛型,继续看看常用的类和包及其对应的数据结构,或是相关的用途。

Java作为一门语言,说实话执行并不高效,但好在灵活,有很多现成的东西能用,互联网具有时效性,经不起数年的慢慢开发,灵活是要优先于效率的,这点取舍大家相比清楚。比如如果想写爬虫,优先选择肯定是Python而不是C,爬虫可能有想法了今晚就要用,经不起慢慢来。Java的灵活性靠的便是现有的类、包和一堆框架,可谓不学不行


Collection 接口的常用程度应该是最高的,作为Java Collection Framework的主要部分,其所包含的Set和List类是最为常用的。

但是在谈Collection之前,还需要了解下Iterable<T>接口,这个接口又称为迭代器,它和Java的foreach遍历有关,实现这个接口的类才能使用foreach实现遍历输出,实现了Iterable<T>的类还需要在内部实现实现自己的迭代器,一般来讲实现Iterator<T>接口的都是集合,除非你自己写个数据结构,否则用到这个接口的概率还真不大。

public class Testclass<E> implements Iterable<E>{

	@Override
	public Iterator iterator() {
		return new TestIterator<E>();
	}
	private class TestIterator<T> implements Iterator<T>{

		@Override
		public boolean hasNext() {
			return false;
		}

		@Override
		public T next() {
			return null;
		}
		
	}
}

当然数组默认也支持foreach遍历,跟你往里面塞什么东西没关系,但是也别轻易用,经测试,foreach输出数组比for循环要慢。 

public class testProgram {
	class Boy{
	}
	public static void main(String[] args) {
		//TODO
		Boy[] a=new Boy[10];
		for (Boy b : a) {//数组类型默认支持foreach
			System.out.println(b);
		}
	}
}

 

而还有一点需要说明的是,关于Collection接口的重要方法——hashCode(),它的作用就是用来判断两个集合是否相等

The hash code of a set is defined to be the sum of the hash codes of the elements in the set, where the hash code of a nullelement is defined to be zero. This ensures that s1.equals(s2) implies that s1.hashCode()==s2.hashCode() for any two sets s1 and s2, as required by the general contract of Object.hashCode().


(一)Set<E>——集合 

Set<E>,是不包含重复元素的集合,可以包含null值,但是只能有一个null值(视具体情况而定),这点和数学上的定义一致。一个Set禁止包含对自身的引用。集合的一些实现类可能禁止空值,可能限定了能装进去的元素。以下是最常用的

(1)HashSet<E>——哈希表相关

没有排序过就存起来的Set,无序。源码中可以看到,它基于HashMap类实现,也就是说当你创建一个HashSet实例,实际上创建了也个HashMap,不过别担心浪费空间,当你向其中添加值的时候,是添加了HashMap的key,而key所对应的value都是指向同一个Object类。

public class TreeSet<E> extends AbstractSet<E>
    implements NavigableSet<E>, Cloneable, java.io.Serializable
{
    private transient HashMap<E,Object> map;
    private static final Object PRESENT = new Object();

...
    public HashSet() {
        map = new HashMap<>();
    }
    public boolean add(E e) {
        return map.put(e, PRESENT)==null;
    }
...
}

具体:

基于哈希表的 Map 接口的NavigableMap实现。此实现提供所有可选的映射操作,并允许使用 null值和 null 键。(除了非同步和允许使用 null 之外,HashMap 类与 Hashtable 大致相同。)此类不保证映射的顺序,特别是它不保证该顺序恒久不变。

此类为基本操作提供了稳定性能,这些基本操作包括 add、remove、contains 和 size

(2)TreeSet<E>——红黑树相关

排序过的集合,有序。顾名思义,此集合实现方式应该为树,学过数据结构可以知道,使用树可以实现查找的log(n)时间开销,而通过查看源代码可以发现,类似HashSet,TreeSet是利用TreeMap实现的,而TreeMap则使用了红黑树(source)。

public class TreeSet<E> extends AbstractSet<E>
    implements NavigableSet<E>, Cloneable, java.io.Serializable
{
    /**
     * The backing map.
     */
    private transient NavigableMap<E,Object> m;
    // Dummy value to associate with an Object in the backing Map
    private static final Object PRESENT = new Object();

    TreeSet(NavigableMap<E,Object> m) {
        this.m = m;
    }

    public TreeSet() {
        this(new TreeMap<E,Object>());
    }
...
}

具体: 

基于 TreeSet 的 NavigableSet实现。

此实现为基本操作(addremove 和 contains)提供受保证的 log(n) 时间开销。

(二)List<E>——数据结构:表(有序集合)

List,又名列表,又称为有序集合,实现了这个接口的类都有一个特点,就是有序,这个有序指的是,你放进去的顺序,但和Set不同的是,列表通常允许重复元素。

(1)ArrayList——表

内部实现是数组,以及对数组的操作,所以经常需要访问它时,它的效率优于LinkedList,与之相对的,它对随机大规模删除、指定位置的插入等并不上心(因为要对数组整体进行移动,而不是单单修改一下引用)。

public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
    ...
    private static final int DEFAULT_CAPACITY = 10;

    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

    transient Object[] elementData; // non-private to simplify nested class access

    private int size;

    public ArrayList(int initialCapacity) {
        if (initialCapacity > 0) {
            this.elementData = new Object[initialCapacity];
        } else if (initialCapacity == 0) {
            this.elementData = EMPTY_ELEMENTDATA;
        } else {
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        }
    }
    public E remove(int index) {
        rangeCheck(index);

        modCount++;
        E oldValue = elementData(index);

        int numMoved = size - index - 1;
        if (numMoved > 0)
            System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);
        elementData[--size] = null; // clear to let GC do its work

        return oldValue;
    }
}

(2)LinkedList——双链表

内部实现是许多个Node,然后通过对下一个以及上一个Node的引用来链接起来,形如:Root-->Node2-->Node3,所以可以知道,LinkedList在具体取出一个项目时,要不断地寻址,所以访问速度不如ArrayList,但是优在灵活,add,remove操作较快。

Node<E>内部类,first和last分别指向上一个和下一个Node

插入时检查是否是新表,如果是,last必为null,则头(first)为新元素,size++。

public class LinkedList<E>
    extends AbstractSequentialList<E>
    implements List<E>, Deque<E>, Cloneable, java.io.Serializable
{
    transient int size = 0;

    /**
     * Pointer to first node.
     */
    transient Node<E> first;

    /**
     * Pointer to last node.
     */
    transient Node<E> last;


    public LinkedList() {
    }
    void linkLast(E e) {
        final Node<E> l = last;
        final Node<E> newNode = new Node<>(l, e, null);
        last = newNode;
        if (l == null)
            first = newNode;
        else
            l.next = newNode;
        size++;
        modCount++;
    }
    public boolean add(E e) {
        linkLast(e);
        return true;
    }
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值