集合
集合和数组是一样,都是用来存放数据
-
数组需要手动扩容
-
集合不需要,而且功能更加强大
1. Collection集合
Collection是一个接口,定义集合这类事物有啥方法和功能或者作用
public interface Collection<E> extends Iterable<E> { //元素个数 int size(); //是否为空 boolean isEmpty(); //是否包含某个值 boolean contains(Object o); //迭代器 遍历元素 Iterator<E> iterator(); //转化为数组 Object[] toArray(); <T> T[] toArray(T[] a); //添加值 boolean add(E e); //删除 boolean remove(Object o); boolean containsAll(Collection<?> c); boolean addAll(Collection<? extends E> c); boolean removeAll(Collection<?> c); //清空 void clear(); boolean equals(Object o); }
2. List集合
List集合是Collection的子接口,是有序可重复的集合
-
有序:添加的每个元素(值)都有一个下标,下标从0开始,依次递增1
-
可重复:元素/值,是可以重复的
public interface List<E> extends Collection<E> { //可以通过下标获取元素的值 E get(int index); }
package com.qfedu; import java.util.ArrayList; import java.util.Collections; import java.util.List; public class Demo02 { public static void main(String[] args) { List<Integer> list = new ArrayList<>(); // ctrl+shift+o //添加 list.add(1); list.add(3); list.add(2); list.add(1); //获取集合的元素个数 int size = list.size(); //遍历 for(int i=0; i<size; i++) { System.out.println(list.get(i)); } //排序 Collections.sort(list); System.out.println(list); } }
2.1 ArrayList集合
ArrayList是List集合的实现类,ArrayList底层是一个数组
-
当第一次添加元素时,ArrayList中elementData属性扩容到10
-
elementData的数据类型为Object[],就是真正存储数据的地方
-
-
第2,3,4,....,10添加,数组都不会扩容
-
第11次添加,原来数组的容量是10,不够用了,扩容1.5倍,扩容为长度为15的数组,执行添加操作
-
ArrayList数组当原来的数组不够用的时候,才会扩容
-
ArrayList源码分析
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[] EMPTY_ELEMENTDATA = {}; //3. 默认空数组,在通过无参构造方法创建ArrayList对象时,赋值给elementData private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; //1. ArrayList存放添加值的地方 它是一个Object类型的数组 transient Object[] elementData; private int size; //ArrayList集合中的元素也是有上限的 private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8; 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); } } //2. 无参构造方法 执行无参构造方法,创建ArrayList对象 public ArrayList() { //初始化elementData this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; } //4.执行添加元素操作 public boolean add(E e) { //5. 确认数组的长度是否够用 ensureCapacityInternal(size + 1); //9. 真正的添加操作 elementData[size++] = e; return true; } //5. 确认数组长度 private void ensureCapacityInternal(int minCapacity) { //7. 判断是扩容 ensureExplicitCapacity( calculateCapacity(elementData, minCapacity) ); } /* * 6、 计算容量 * - 当第一次执行时,返回值为10 * - 以后每次,都是直接返回minCapacity值 */ private static int calculateCapacity(Object[] elementData, int minCapacity) { //只有当第一次添加添加元素时,elementData才为空数组 if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { return Math.max(DEFAULT_CAPACITY, minCapacity); } return minCapacity; } //7. 判断容量是否扩容 private void ensureExplicitCapacity(int minCapacity) { modCount++; // 只要在数组不够用的时候,才会进行扩容 if (minCapacity - elementData.length > 0) { //8. 扩容数组 grow(minCapacity); } } //8. 扩容数组 private void grow(int minCapacity) { // 获取原来数组的长度 0,10 int oldCapacity = elementData.length; //计算后的新长度 0,15 int newCapacity = oldCapacity + (oldCapacity >> 1); //只有第一次添加的时候才会成立 if (newCapacity - minCapacity < 0) newCapacity = minCapacity; //判断数组的容量是否达到最大值 if (newCapacity - MAX_ARRAY_SIZE > 0) newCapacity = hugeCapacity(minCapacity); // 扩容数组, 第一次时,扩容为长度为10,以后每次扩容为原来的1.5倍 elementData = Arrays.copyOf(elementData, newCapacity); } }
2.2 LinkedList集合
LinkedList是List接口的一个实现类,底层是一个链表,是一个双向链表
链表是由一个一个的链子组成,链子就是Node对象
public class LinkedList<E> extends AbstractSequentialList<E> implements List<E>, Deque<E>, Cloneable, java.io.Serializable { transient int size = 0; //链表的第一个链子 transient Node<E> first; //链表的最后一个链子 transient Node<E> last; public LinkedList() { } //添加元素 public boolean add(E e) { linkLast(e); return true; } void linkLast(E e) { //获取最后一个链子(Node对象) 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; } } }
2.3 Vector集合
Vector的底层也是一个数组,只不过它是线程安全的。
2.4 比较ArrayList和LinkedList
ArrayList底层是数组:查询的速度快,删除,和插入的速度慢
LinkedList底层是一个链表:查询效率低,删除和插入的速度快
Vector是线程安全的,效率比ArrayList低,适合使用在多线程的编程条件下。
3. Set集合
Set是Collection的子接口,无序不可重复
无序:有顺序,没有序号
不可重复:值不能重复(可以自定义重复的规则)
public interface Set<E> extends Collection<E> { //基本和Colletion接口一样 }
3.1 HashSet
HashSet是Set集合的一个实现类,底层是HashMap
可以使用增强型for循环进行遍历
package com.qfedu; import java.util.HashSet; import java.util.Iterator; import java.util.Set; public class Demo06 { public static void main(String[] args) { /* * Set集合 * HashSet实现 */ //初始化 Set<Integer> set = new HashSet<Integer>(); //添加元素 set.add(9); set.add(19); set.add(18); set.add(76); /* * 增强型for循环 * for(泛型 变量 : 集合) { System.out.println(变量); } 集合会把元素依次赋值给变量,在for循环中执行 * */ for(Integer i : set) { System.out.println(i); } } }
3.2 TreeSet
TreeSet也是Set的实现类,会自动为元素进行排序
注意 : TreeSet是为元素排序的一个非常好的一种方法,
如果实现一个类的两个对象是可比较的,比较的目的是为了排序
如何定义两个对象比较的规则,这个类需要实现Comparable接口
我们常见的一些类,比如Integer,String等类都实现了Comparable接口定义了两个对象比较的方式
public interface Comparable<T> { /* * 正整数>你 零 =你 负整数<你 */ public int compareTo(T o); }
package com.qfedu; import java.util.Set; import java.util.TreeSet; public class Demo06_2 { public static void main(String[] args) { /* * Set集合 * TreeSet实现类:TreeSet会自动为元素排序 */ Set<Integer> set = new TreeSet<Integer>(); //添加元素 set.add(9); set.add(19); set.add(10); set.add(76); for(Integer i : set) { System.out.println(i); } Set<Student> set2 = new TreeSet<Student>(); set2.add(new Student(99, "jackma","111")); set2.add(new Student(100, "pony","121")); set2.add(new Student(99, "tomlei","131")); set2.add(new Student(98, "james","131")); for(Student s : set2) { System.out.println(s); } } } class Student implements Comparable<Student>{ private Integer score; private String name; private String code; // 学号 public Student() { } public Student(Integer score, String name) { super(); this.score = score; this.name = name; } public Student(Integer score, String name, String code) { super(); this.score = score; this.name = name; this.code = code; } public Integer getScore() { return score; } public void setScore(Integer score) { this.score = score; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getCode() { return code; } public void setCode(String code) { this.code = code; } @Override public String toString() { return "Student [score=" + score + ", name=" + name + ", code=" + code + "]"; } @Override public int compareTo(Student o) { //根据成绩进行排序 // return this.score - o.getScore(); //根据名字排序 // return this.name.length() - o.getName().length(); //根据成绩降序排 // return o.getScore() - this.score ; /* * 先根据姓名的长度进行排序 * 1. 如果学生的姓名长度是相等,再根据成绩进行排序 * 2. 如果成绩也相等,那就根据学号进行排序 */ if(this.name.length() != o.getName().length() ) { return this.name.length() - o.getName().length(); } else if(this.score != o.getScore()) { return this.score - o.getScore(); } else { /* * 字符串的比较 * 参考源码,从第一个字符开始比较,相减 */ return this.code.compareTo(o.getCode()); } } }
4. 内部类
-
成员内部类
-
直接定义在类中
-
-
静态内部类
-
由static修饰的成员内部类
-
-
局部内部类
-
定义在方法中的类
-
-
匿名内部类
-
直接把接口的实现定义在接口的实例化中
-
package com.qfedu; public class Demo03 { private String a; private String b; private static int c; //1. 成员内部类,直接定义在类中 class InnerClass { private String d; // private static String e; //不允许创建静态属性 public void print() { System.out.println(a+c); } } //2. 静态内部类,由static修饰的成员内部类 static class InnerStaticClass { //可以定义静态属性 private static String e; //可以操作外部类静态属性 public void print() { System.out.println(e+c); } } public void test() { //3. 定义在方法中类,局部内部类 class PartClass { } PartClass pc = new PartClass(); } public static void main(String[] args) { A a = new B(); a.aa(); //4. 匿名内部类,不考虑代码的可复用性,就爽这一次 A a2 = new A() { @Override public void aa() { System.out.println("在这里实现aa抽象方法---"); } }; a2.aa(); } } interface A { void aa(); } class B implements A { @Override public void aa() { System.out.println("实现aa"); } }
5. Map集合
Map是一种键值对(K-V),每一个值都会有一个名字
public interface Map<K,V> { //键值对个数 int size(); boolean isEmpty(); //是够存在key boolean containsKey(Object key); //是够存在value boolean containsValue(Object value); V get(Object key); V put(K key, V value); V remove(Object key); void clear(); //获取所有的key得到一个set集合 Set<K> keySet(); //获取键值对的集合 Set<Map.Entry<K, V>> entrySet(); //内部类 表示一个对 interface Entry<K,V> { K getKey(); V getValue(); } }
package com.qfedu; import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; import java.util.Set; public class Demo07 { public static void main(String[] args) { /* * 定义一个Map集合 * K : String 表示键只能定义字符串 * V : Integer 只能保存整数的值 * * 每次在保存值时,保存是一对,有名字有值 */ Map<String, Integer> map = new HashMap<String, Integer>(); /* * 保存值 * * put(k,v); * * k:不允许重复,后面添加的会覆盖掉前面的值 * k,v值可以为null * */ map.put("jackma", 100); map.put("pony", 90); map.put("tomlei", 120); map.put("tomlei", 130); map.put(null, null); System.out.println(map.size()); System.out.println(map.get("tomlei")); /* * 获取值 * * 可以根据key获取对应的value * * V get(Object key); */ Integer v1 = map.get("jackma"); Integer v2 = map.get("kkk"); System.out.println(v1); System.out.println(v2); // 没有该key,返回null System.out.println("-------------------------"); //遍历 先获取k,然后通过k获取v Set<String> keySet = map.keySet(); for(String k : keySet) { System.out.println(k + ":"+ map.get(k)); } System.out.println("-------------------------"); Set<Entry<String, Integer>> entrySet = map.entrySet(); for(Entry<String, Integer> e : entrySet) { System.out.println(e.getKey()+":"+ e.getValue()); } } }
5.1 HashMap
HashMap的底层是数组加链表(单向链表)
public class HashMap<K,V> extends AbstractMap<K,V> implements Map<K,V>, Cloneable, Serializable { //初始化容量 static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // 16 //最大容量 static final int MAXIMUM_CAPACITY = 1 << 30; //加载因子 扩容的时机 static final float DEFAULT_LOAD_FACTOR = 0.75f; //树化的阈值 static final int TREEIFY_THRESHOLD = 8; //反树化的阈值 static final int UNTREEIFY_THRESHOLD = 6; //树化的最小容量 static final int MIN_TREEIFY_CAPACITY = 64; //链表的数组 HashMap的底层的主结构就是链表的数组 transient Node<K,V>[] table; //键值对的个数 transient int size; //链表 单项链表 static class Node<K,V> implements Map.Entry<K,V> { final int hash; final K key; V value; Node<K,V> next; Node(int hash, K key, V value, Node<K,V> next) { this.hash = hash; this.key = key; this.value = value; this.next = next; } } //红黑树 static final class TreeNode<K,V> extends LinkedHashMap.Entry<K,V> { TreeNode<K,V> parent; // red-black tree links TreeNode<K,V> left; TreeNode<K,V> right; TreeNode<K,V> prev; // needed to unlink next upon deletion boolean red; TreeNode(int hash, K key, V val, Node<K,V> next) { super(hash, key, val, next); } } }