集合

集合类结构图:

图片来源于网络,侵删。

Collection

List和Set接口的父接口,继承了Iterator接口,所以List,Set均可以使用迭代器输出。定义了add(),remove()这些方法,不同类型的集合,对collection中方法的实现有所不同。

que:Collection和Collections的区别?

Collection是集合的root interface,定义了集合的一些基础方法,Collections像是一个工具类,定义了与集合相关的方法,一般来说Collection的子类集合(arrayList,hashMap等)都是非线程安全的,就可以通过Collections来获得相应的线程安全集合。令外Collections还提供unmodifiableMap/List/Set()方法来保证集合不可被修改。

·List

有序集合(sequence),允许重复(duplicate)元素,允许多个null元素。
另外值得提一嘴的是,List有一个单独的迭代器:ListIterator。

-ArrayList

非线程安全。可以通过Collections获得一个synchronizedList。
有序,允许重复元素和null元素。
继承了List和AbstractList,是一个长度可变的数组(resizable-array)。默认长度为10扩容方案为原来的1.5倍。
底层就是用数组来实现的,这决定了其查询快的特点。
ArrayList的capacity就是数组的size。

看一下源码:

    /**
     * Default initial capacity.
     */
    private static final int DEFAULT_CAPACITY = 10;

    /**
     * Shared empty array instance used for empty instances.
     */
    private static final Object[] EMPTY_ELEMENTDATA = {};

    /**
     * Shared empty array instance used for default sized empty instances. We
     * distinguish this from EMPTY_ELEMENTDATA to know how much to inflate when
     * first element is added.
     */
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

    /**
     * Constructs an empty list with an initial capacity of ten.
     */
    public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }

如果在创建ArrayList时没有指定initialCapacity,会创造一个默认DEFAULT_CAPACITY为10的数组。

另外在源码中说明: (This class is roughly equivalent to Vector, except that it is unsynchronized.)
Vector(已过时)是线程安全的ArrayList,其中所有方法都加了synchronized。此外二者的区别还在于扩容方案,Vector的扩容方案为原来两倍。

-LinkedList

底层实现是双向链表(Doubly-linked),所以查询慢,增删快。因为在查询时只能从头一个一个遍历元素。
在eclipse中查看LinkedList类的outline,可以看到有first,last,node这些,可以实锤是链表。
有序,允许重复元素和Null元素。
非线程安全,同样是通过collections.synochronizedList(list)得到一个线程安全的linkedList。

List集合代码:

//list有序集合,可以重复,可为null
public class ListTest {

	public static void main(String[] args) {

		// ArrayList底层实现是长度可以改变的数组,查询快,增删慢
		List<String> list1 = new ArrayList<>();

		// LinkedList底层实现是链表,查询慢,增删快
		// 提供了专门对头尾元素的操作
		List<String> list2 = new LinkedList<>();

		list1.add("hehe");
		list2.add("haha");

		// 1.for-each输出
		for (String str1 : list1) {
			System.out.println(str1);
		}
		// 2.iterator输出,Iterator只能向后输出,ListIter可向前(previous())、向后输出
		Iterator<String> iterator = list1.iterator();
		while (iterator.hasNext()) {
			String str2 = iterator.next();
			System.out.println(str2);
		}
		// ListIterator输出
		ListIterator<String> listIterator = list2.listIterator();
		while (listIterator.hasNext()) {
			String str3 = (String) listIterator.next();
			System.out.println(str3);
		}

		// for循环输出
		for (int i = 0; i < list1.size(); i++) {
			System.out.println(list1.get(i) + "----");
		}

	}
}

que:Iterator和ListIterator的区别?

ListIterator接口继承(extends)Iterator接口,查看二者源码,可以看到ListIterator对Iterator做了扩展,额外提供了对index的操作和上一个元素的操作,previous()获取上一个元素,可以实现向前遍历集合。但要注意向前遍历时,index不能处于第一个位置。要确保这一点,可以在获取listIterator(int)时指定index位置。

ps:remove()方法:

    /**
     * Removes from the list the last element that was returned by {@link
     * #next} or {@link #previous} (optional operation).  This call can
     * only be made once per call to {@code next} or {@code previous}.
     * It can be made only if {@link #add} has not been
     * called after the last call to {@code next} or {@code previous}.
     *
     * @throws UnsupportedOperationException if the {@code remove}
     *         operation is not supported by this list iterator
     * @throws IllegalStateException if neither {@code next} nor
     *         {@code previous} have been called, or {@code remove} or
     *         {@code add} have been called after the last call to
     *         {@code next} or {@code previous}
     */
    void remove();

看remove()方法的说明,注意抛出IllegalStateException异常的情况:如果previous()或next()方法没有被调用,或是在调用previous()或next()方法之后已经调用过add()或是remove()方法,调用remove()会报异常IllegalStateException。

ListIterator独有的set()方法与remove()类似。

·Set

不可重复(no dulicate elements),JDK文档指出Set判断元素是否重复依据的是equals()的比较结果,但还要考虑到hashCode。
无序,最多允许一个null元素(TreeSet不允许Null元素)
对于重复元素的判断,要同时判断equals()和hashCode。
Set接口的底层是根据Map接口实现的:TreeSet-TreeMap,HashSet-HashMap

-HashSet

继承自Set接口,其底层实际是一个HashMap,查看其源码可以看到HashSet的构造方法是直接new一个HashMap(或是LinkedHashMap)出来。
至多一个null element,多个判定为重复元素。可以指定初始容量int initialCapacity,默认为16,扩容方案为原来2倍load factor默认为0.75
HashSet中的元素就是HashMap的key,key不可以重复,可以为null,对应HashSet元素的属性,可以为null,不可以重复。

HashSet构造方法源码:

    /**
     * Constructs a new, empty set; the backing <tt>HashMap</tt> instance has
     * default initial capacity (16) and load factor (0.75).
     */
    public HashSet() {
        map = new HashMap<>();
    }

load factor:加载因子,hashSet达到0.75的容量时进行扩容,扩容方案为当前容量的2倍

que:HashSet是如何判断元素是否重复?

先说结论:只有同时满足1)equals()返回true和2)hashCode相同两个条件,HashSet才会认定是重复元素。所以在存入对象时,必须要重写equals()和hashCode()两个方法。这样才能达到开发者不让HashSet(HashMap)中的元素重复的要求。

HashSet/HashMap判断元素是否重复的依据是equals()方法,但是由于其底层是一个hash table,而hash table是根据hashCode来认定元素是否重复的,所以就导致这样的情况:equals()的结果返回为false,认定为两个不同对象,如果equals()返回为true,本应该认定为同一个对象,但是如果两个对象hashCode不同,在hash table中仍然被认为两个元素,所以就是即便equals()返回为true,却还是在hashSet中存入了两个相同元素。

说起来有点费解,可移步:https://www.cnblogs.com/skywang12345/p/3324958.html

-LinkedHashSet

文档中的说明:This implementation differs from HashSet</tt> in that it maintains a doubly-linked list running through all of its entries.
即LinkedHashSet和HashSet的区别是在hash算法的基础上多了一个双向链表(doubly-linked list)。
同hashSet一样,可以有一个null元素,初始容量为16,load factor为0.75
继承HashSet类、实现Set接口,底层其实就是LinkedHashMap,可以查看源码:

public class LinkedHashSet<E>
    extends HashSet<E>
    implements Set<E>, Cloneable, java.io.Serializable {

    private static final long serialVersionUID = -2851667679971038690L;

    /**
     * Constructs a new, empty linked hash set with the specified initial
     * capacity and load factor.
     *
     * @param      initialCapacity the initial capacity of the linked hash set
     * @param      loadFactor      the load factor of the linked hash set
     * @throws     IllegalArgumentException  if the initial capacity is less
     *               than zero, or if the load factor is nonpositive
     */
    public LinkedHashSet(int initialCapacity, float loadFactor) {
        super(initialCapacity, loadFactor, true);
    }

//点进super(),到HashSet的构造方法,可以看到是直接创建一个LinkedHashMap:

     /**
     * Constructs a new, empty linked hash set.  (This package private
     * constructor is only used by LinkedHashSet.) The backing
     * HashMap instance is a LinkedHashMap with the specified initial
     * capacity and the specified load factor.
     *
     * @param      initialCapacity   the initial capacity of the hash map
     * @param      loadFactor        the load factor of the hash map
     * @param      dummy             ignored (distinguishes this
     *             constructor from other int, float constructor.)
     * @throws     IllegalArgumentException if the initial capacity is less
     *             than zero, or if the load factor is nonpositive
     */
    HashSet(int initialCapacity, float loadFactor, boolean dummy) {
        map = new LinkedHashMap<>(initialCapacity, loadFactor);
    }

-TreeSet

底层是通过treeMap来实现的,可以看到直接new一个treeMap出来。
存入其中的元素会被排序,通过比较器Comparator或者Comparable接口的compareTo()方法,在使用时,必须覆写compareTo()方法。
元素不可重复元素不可以为null,非线程安全。
TreeSet在使用的时候必须要传入Comparator或者元素必须继承Comparable接口并复写CompareTo()方法,可看作是定义排序规则。
ps:集合中存放的都是对象,TreeSet存入简单类型(int,char)等,也会转成Integer,而Integer类本身已经实现了Comparable接口,复写了compareTo()方法。而如果是存入自定义对象,一定要重写compareTo()方法,实现排序。

Set集合代码:

//Set无序不重复,不可通过下标来访问,元素在set中的位置由其hash码决定,位置固定;至多一个null元素(TreeSet除外);不允许重复
public class SetTest {
	public static void main(String[] args) {
		
		//hashSet,当查询时根据哈希码来比较元素,查询速度快
		//底层是用hashMap实现
		Set<String> hashSet = new HashSet<>();
		hashSet.add("heihei");
		
		//linkedHashSet,底层采用链表和哈希表算法,链表查询慢,增删快
		Set<String> linkedHashSet = new LinkedHashSet<>();
		linkedHashSet.add("xixi");
		//多个Null判定为元素重复
		linkedHashSet.add(null);
		linkedHashSet.add(null);
		
		System.out.println(linkedHashSet.size());
		
		//treeSet,红黑树算法,擅长范围查询;不允许null;不保证元素的添加顺序,但会对存入其中的元素进行排序
		//底层是用treeMap实现
		Set<String> treeSet = new TreeSet<>();
		treeSet.add("lalala");
		
		//解决Set实现类线程不安全:以hashSet为例
		Set<String> synchronizedSet = Collections.synchronizedSet(hashSet);
		for (String string : synchronizedSet) {
			System.out.println(string);
		}
		
		Iterator<String> iterator = hashSet.iterator();
		while (iterator.hasNext()) {
			Object object = (Object) iterator.next();
			System.out.println(object);
		}
		
		//for循环,将set转换为数组后for输出
		String setArray[] = new String[hashSet.size()];
		hashSet.toArray(setArray);
		for (int i = 0; i < setArray.length; i++) {
			System.out.println(setArray[i]+"---");
		}
	}
}

·Map

key-value形式,key与value一一对应,key不可以重复,可以为null(只能有一个?这样说是因为HashSet只能有一个null)
本质上 Map 并不是一个集合,而是两个集合之间的映射关系。这两个集合每一条数据通过映射关系,可以看成是一条数据。
即 Entry(key,value)。Map 可以看成是由多个 Entry 组成。
注意Map并没有继承Iterator接口,其继承类也都没有实现Iterator接口
注意一个方法:Set<Map.Entry<K, V>> entrySet(),Entry是用来存放键值对(key-value pair)的
这个方法返回一个泛型为Entry的Set集合,Map没有实现Iteraotr接口,但是可以用Iterator来遍历Set<Entry>,这样就可以输出Map中的内容。

-HashMap

允许key和value均为null,key不可以重复,只能有一个null
非线程安全,Connections.synchronizedMap()获得一个线程安全的hashMap
在创建HashMap时可以加入initial capacity、load factor两个参数:默认初始容量为16load factor为0.75

que:HashMap与HashTable(已弃用)的区别?
源码:The <tt>HashMap</tt> class is roughly equivalent to <tt>Hashtable</tt>, except that it is unsynchronized and permits nulls.
即HashMap为非线程安全,HashTable是线程安全的,HashMap允许key和value都为空,HashTable不允许null。

-LinkedHashMap

继承了HashMap类,实现Map接口,允许key和value均为null

-TreeMap

非线程安全
会对元素根据key进行排序,元素不可为null
存入自定义对象需要Comparator或是实现Comparable接口,覆写compareTo()方法

Map操作代码:

//Map
public class MapTest {
	public static void main(String[] args) {
		
		//hashMap,哈希算法,查询效率高,允许一个key为null,多个value为null
		Map<String, String> hashMap = new HashMap<>();
		hashMap.put("1", "hehe");
		hashMap.put("2", "haha");
		//linkedHashMap,哈希算法和链表,链表增删快,查询慢
		Map<String, String> linkedHashMap = new LinkedHashMap<>();
		
		//红黑树算法,范围查询
		Map<String, String> treeMap = new TreeMap<>();
		
		//根据key获取value
		System.out.println(hashMap.get("1"));
		
		//获取所有key保存到set
		Set<String> keys = hashMap.keySet();
		for (String str : keys) {
			System.out.println(str);
		}
		
		//获取所有value保存到collection
		Collection<String> values = hashMap.values();
		Iterator<String> iterator = values.iterator();
		while (iterator.hasNext()) {
			System.out.println(iterator.next());
		}
		
		//获取所有entrys输出
		Set<Entry<String, String>> entrys = hashMap.entrySet();
		Iterator<Entry<String, String>> entryIterator = entrys.iterator();
		while (entryIterator.hasNext()) {
			System.out.println(entryIterator.next());
		}
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值