集合 概念:对象的容器,定义了对多个对象进行操作的常用方法,可实现数组的功能
所有集合类都位于java.util
包下,Java的集合类主要由两个接口派生而出,Collection和Map,Collection和Map是Java集合框架的根接口,这两个接口又包含了一些子接口或实现类。
集合与数组区别:
数组长度固定,集合长度不固定
数组可以存储基本类型和引用类型,集合只能存储引用类型
Collection体系集合
Collection父接口 特点:代表一组任意类型的对象,无序、无下标、不能重复
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 public static void main (String[] args) { Collection collection = new ArrayList (); collection.add("苹果" ); collection.add("香蕉" ); collection.add("梨子" ); collection.remove("苹果" ); collection.clear(); for (Object obj : collection) { System.out.println(obj); } Iterator it = collection.iterator(); while (it.hasNext()){ Object obj = it.next(); it.remove(); } boolean result1 = collection.contains("梨子" ); boolean result2 = collection.inEmpty(); int num = collection.size(); }
Set和List的区别
Set接口存储的是无序、无下标、不重复的数据。List接口存储的是有序的、有下标、可以重复的元素
Set检索效率低下,删除和插入效率高,插入和删除不会引起元素位置改变,实现类有HashSet,TreeSet
List和数组类似,可以动态增长,根据实际存储的数据的长度自动增长List的长度。查找元素效率高,插入删除效率低,因为会引起其他元素位置改变,实现类有ArrayList,LinkedList,Vector
List子接口 特点:有序、有下标、元素可重复
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 public static void main (String[] args) { List list = new ArrayList <>(); list.add("苹果" ); list.add("梨子" ); list.add("香蕉" ); list.remove("梨子" ); list.remove(0 ); for (int i= 0 ; i<list.size(); i++) { System.out.println(list.get(i)); } for (Object obj : list) { System.out.println(obj); } Iterator it = list.iterator(); while (it.hasNext()){ Object obj = it.next(); it.remove(); } ListIterator it1 = list.listIterator(); while (it1.hasNext()){ System.out.println(); } while (it1.hasNext()){ System.out.println(); } System.out.println(list.indexOf("香蕉" )); list.add(10 ); list.add(20 ); list.remove(list.indexOf(10 )); List subList = list.subList(0 ,2 ); String[] array =new String [list.size()]; list.toArray(array); }
List实现类
ArrayList:数组结构实现,必须要连续空间,查询快,增删慢,运行效率快,线程不安全
Vector:数组结构实现,查询快,增删慢,运行效率慢,线程安全
LinkedList:双向链表结构实现,增删快,查询慢
ArrayList 源码分析:
如果没有向集合中添加任何元素时,容量0,添加一个后,容量为10
首次添加元素时,ArrayList会进行第一次扩容,之后每当判断到容量不够时,就会扩容,每次扩容是原来的1.5倍
1 2 3 4 5 6 7 8 9 10 11 private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; private static final int DEFAULT_CAPACITY = 10 ; private int size; transient Object[] elementData;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 public ArrayList () { this .elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; } public boolean add (E e) { ensureCapacityInternal(size + 1 ); elementData[size++] = e; return true ; } private void ensureCapacityInternal (int minCapacity) { ensureExplicitCapacity(calculateCapacity(elementData, minCapacity)); } private static int calculateCapacity (Object[] elementData, int minCapacity) { if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { return Math.max(DEFAULT_CAPACITY, minCapacity); } return minCapacity; } private void ensureExplicitCapacity (int minCapacity) { modCount++; if (minCapacity - elementData.length > 0 ) grow(minCapacity); } private void grow (int minCapacity) { int oldCapacity = elementData.length; int newCapacity = oldCapacity + (oldCapacity >> 1 ); if (newCapacity - minCapacity < 0 ) newCapacity = minCapacity; if (newCapacity - MAX_ARRAY_SIZE > 0 ) newCapacity = hugeCapacity(minCapacity); elementData = Arrays.copyOf(elementData, newCapacity); }
Vector 添加、删除、判断都与List子接口相同,遍历使用枚举器
1 2 3 4 5 6 Vector vector = new Vector ();Enumeration en = vector.elements();while (en.hasMoreElements()){ String o = (String)en.nextElement(); System.out.println(o); }
LinkedList 常用方法都与List子接口相同 源码分析:
首次添加元素之后,first以及last都会指向第一个节点
之后每次添加元素,first始终指向第一个节点,last会指向当前节点
每个节点中的next属性存储下一个节点,prev属性存储上一个节点
1 2 3 4 5 6 7 8 transient int size = 0 ;transient Node<E> first;transient Node<E> last;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 public boolean add (E e) { linkLast(e); return true ; } 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++; } private static class Node <E> { E item; Node<E> next; Node<E> prev; Node(Node<E> prev, E element, Node<E> next) { this .item = element; this .next = next; this .prev = prev; } }
Set子接口 特点:无序、无下标、元素不可重复 方法:全部继承自Collection父接口中的方法,添加、删除、遍历、判断与collection父接口中一致
Set实现类
HashSet:基于HashCode计算元素存放位置,当在同一个位置存入的两个元素哈希码相同时,会调用equals再次进行确认,对比两个元素的内存地址,如果也为true,则判定为同一个元素,拒绝后者存入,如果为false,说明不是同一个元素,则在此位置形成链表
TreeSet:基于排列顺序实现元素不重复,实现了SortedSet接口,对集合元素自动排序,元素对象的类型必须实现Comparable接口,指定排序规则,通过CompareTo方法确定是否为重复元素
HashSet 存储结构:哈希表(数组+链表+红黑树)
重写hashCode()和equals()方法,可以自定义hash的计算规则,从而改变结果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 @Override public boolean equals (Object o) { if (this == o) return true ; if (o == null || getClass() != o.getClass()) return false ; User user = (User) o; if (age != user.age) return false ; return name != null ? name.equals(user.name) : user.name == null ; } @Override public int hashCode () { int result = name != null ? name.hashCode() : 0 ; result = 31 * result + age; return result; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 public static void main (String[] args) { HashSet<String> hashSet = new HashSet <String>(); hashSet.add("苹果" ); hashSet.add("梨子" ); hashSet.add("香蕉" ); hashSet.remove("梨子" ); for (Object obj : hashSet) { System.out.println(obj); } Iterator it = hashSet.iterator(); while (it.hasNext()){ System.out.println(it.next()); it.remove(); } System.out.println(hashSet.toString()); }
TreeSet 存储结构:红黑树 要求:使用TreeSet存储引用类型数据时,元素需要要实现Comparable接口,重写compareTo()方法,方法返回值为0,认为是重复元素
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 public class Student implements Comparable <Student>{ private String name; private int age; public Student () { } public Student (String name, int age) { this .name = name; this .age = age; } public String getName () { return name; } public void setName (String name) { this .name = name; } public int getAge () { return age; } public void setAge (int age) { this .age = age; } @Override public String toString () { return "Student{" + "name='" + name + '\'' + ", age=" + age + '}' ; } @Override public int compareTo (Student o) { int n1 = this .getName().compareTo(o.getName()); int n2 = this .age - o.getAge(); return n1==0 ? n2: n1; } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public class Test { public static void main (String[] args) { TreeSet<Student> treeSet = new TreeSet <>(); Student stu1 = new Student ("张三" ,20 ); Student stu2 = new Student ("李四" ,21 ); Student stu3 = new Student ("张三" ,22 ); treeSet.add(stu1); treeSet.add(stu2); treeSet.add(stu3); System.out.println(treeSet.size()); System.out.println(treeSet.toString()); } }
也可以不继承Comparable接口,使用比较器,在创建集合的同时,指定比较规则
1 2 3 4 5 6 7 8 TreeSet<Student> students = new TreeSet <>(new Comparator <Student>() { @Override public int compare (Student stu1, Student stu2) { int n1 = stu1.getName().compareTo(stu2.getName()); int n2 = stu1.getAge() - stu2.getAge(); return n1 = = 0 ? n2: n1; } });
Map体系集合
Map父接口 特点:
用于存储任意键值对(key - value)
键:无序、无下标、不允许重复(唯一)
值:无序、无下标、允许重复
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 public static void main (String[] args) { Map<String, String> map = new HashMap <>(); map.put("cn" , "中国" ); map.put("uk" , "英国" ); map.put("cn" , "zhongguo" ); map.remove("uk" ); for (String key : map.keySet()) { System.out.println("key= " + key + " and value= " + map.get(key)); } Iterator<Map.Entry<String, String>> it = map.entrySet().iterator(); while (it.hasNext()) { Map.Entry<String, String> entry = it.next(); System.out.println("key= " + entry.getKey() + " and value= " + entry.getValue()); } for (Map.Entry<String, String> entry : map.entrySet()) { System.out.println("key= " + entry.getKey() + " and value= " + entry.getValue()); } for (String v : map.values()) { System.out.println("value= " + v); } System.out.println(map.containsKey("cn" )); System.out.println(map.containsValue("中国" )); }
Map实现类
HashMap:线程不安全,运行效率快,允许使用null作为key或是value
Hashtable:线程安全,运行效率慢;不允许null作为key或是value
Properties:hashtable的子类,要求key和value都是string,通常用于配置文件的读取
TreeMap:实现了SortedMap接口(是map的子接口),可以对key自动排序
HashMap 存储结构:哈希表(数组+链表+红黑树) 增、删、遍历、判断与Map父接口一致
源码分析:
HashMap刚创建时,table是null,节省空间,当添加第一个元素时,table容量调整为16
当元素个数大于阈值(16*0.75 = 12)时,会进行扩容,扩容后的大小为原来的两倍,目的是减少调整元素的个数
jdk1.8 当每个链表长度 >8 ,并且数组元素个数 ≥64时,会调整成红黑树,目的是提高效率
jdk1.8 当链表长度 <6 时 调整成链表
jdk1.8 以前,链表时头插入,之后为尾插入
1 2 3 4 5 6 7 8 9 10 11 12 13 14 static final int DEFAULT_INITIAL_CAPACITY = 1 << 4 ;static final int MAXIMUM_CAPACITY = 1 << 30 ;static final float DEFAULT_LOAD_FACTOR = 0.75f ;transient Node<K,V>[] table;transient int size;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 public V put (K key, V value) { return putVal(hash(key), key, value, false , true ); } static final int hash (Object key) { int h; return (key == null ) ? 0 : (h = key.hashCode()) ^ (h >>> 16 ); } final V putVal (int hash, K key, V value, boolean onlyIfAbsent,boolean evict) { Node<K,V>[] tab; Node<K,V> p; int n, i; if ((tab = table) == null || (n = tab.length) == 0 ) n = (tab = resize()).length; if ((p = tab[i = (n - 1 ) & hash]) == null ) tab[i] = newNode(hash, key, value, null ); else { Node<K,V> e; K k; if (p.hash == hash && ((k = p.key) == key || (key != null && key.equals(k)))) e = p; else if (p instanceof TreeNode) e = ((TreeNode<K,V>)p).putTreeVal(this , tab, hash, key, value); else { for (int binCount = 0 ; ; ++binCount) { if ((e = p.next) == null ) { p.next = newNode(hash, key, value, null ); if (binCount >= TREEIFY_THRESHOLD - 1 ) treeifyBin(tab, hash); break ; } if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) break ; p = e; } } if (e != null ) { V oldValue = e.value; if (!onlyIfAbsent || oldValue == null ) e.value = value; afterNodeAccess(e); return oldValue; } } ++modCount; if (++size > threshold) resize(); afterNodeInsertion(evict); return null ; }
Hashtable 线程安全,运行效率慢;不允许null作为key或是value
Properties Hashtable的子类,要求key和value都是string,通常用于配置文件的读取
TreeMap 实现了SortedMap接口(是map的子接口),可以对key自动排序 TreeMap存储引用类型数据的时候,也和TreeSet一样,需要实现Comparable接口,或者是在创建集合的同时,指定比较规则,具体使用,参照TreeSet
Collections工具类 集合工具类,定义了除了存取以外的集合常用方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 public static void main (String[] args) { List<Integer> list = new ArrayList <>(); list.add(20 ); list.add(5 ); list.add(12 ); list.add(30 ); list.add(6 ); System.out.println("排序前:" + list.toString()); Collections.sort(list); System.out.println("排序后:" + list.toString()); int i = Collections.binarySearch(list,12 ); System.out.println(i); List<Integer> dest = new ArrayList <>(); for (int k = 0 ;k < list.size();i++) { dest.add(0 ); } Collections.copy(dest,list); System.out.println(dest.toString()); Collections.reverse(list); System.out.println("反转后:" + list); Collections.shuffle(list); System.out.println("打乱后:" + list); Integer[] arr = list.toArray(new Integer [10 ]); System.out.println(arr.length); System.out.println(Arrays.toString(arr)); String[] names = {"张三" ,"李四" ,"王五" }; List<String> list2 = Arrays.asList(names); System.out.println(list2); }