概述

集合本质上就是一个存储数据的容器,数组就是集合的一种数据类型。集合不能直接存储基本数据类型和Java对象,只能存储Java对象的内存地址(或者叫引用),如下图所示:
集合类 - 图1
Java中不同的集合对应不同的数据结构。往不同的集合中存储元素就是往不同的数据结构中存储元素,这些数据结构包括数组、二叉树、链表、哈希表等。
集合是在java.util.*包下的,集合的整个继承体系是非常庞杂的。总的来说,根据集合体系的元素的存储方式(以单个方式存储元素、以键值对存储元素)的不同,将集合体系分成Collection集合和Map集合,分别在java.util.Collection包和java.util.Map包下。

Collection集合体系继承关系

Collection集合体系如下:
集合类 - 图2集合类 - 图3
注意,Collection是个接口,它继承了Iterator接口,用法如下:
Iterator it = “Collection对象”.iterator();
it是迭代器对象

Map集合体系的继承关系

Map集合体系的继承关系如下:
集合类 - 图4

  1. /**
  2. * The bin count threshold for using a tree rather than list for a
  3. * bin. Bins are converted to trees when adding an element to a
  4. * bin with at least this many nodes. The value must be greater
  5. * than 2 and should be at least 8 to mesh with assumptions in
  6. * tree removal about conversion back to plain bins upon
  7. * shrinkage.
  8. */
  9. static final int TREEIFY_THRESHOLD = 8;
  10. /**
  11. * The bin count threshold for untreeifying a (split) bin during a
  12. * resize operation. Should be less than TREEIFY_THRESHOLD, and at
  13. * most 6 to mesh with shrinkage detection under removal.
  14. */
  15. static final int UNTREEIFY_THRESHOLD = 6;
  16. /**
  17. * The smallest table capacity for which bins may be treeified.
  18. * (Otherwise the table is resized if too many nodes in a bin.)
  19. * Should be at least 4 * TREEIFY_THRESHOLD to avoid conflicts
  20. * between resizing and treeification thresholds.
  21. */
  22. static final int MIN_TREEIFY_CAPACITY = 64;

集合类 - 图5集合类 - 图6

Collection接口

普通常用方法

image.png

  1. package javase.collection;
  2. import java.util.ArrayList;
  3. import java.util.Collection;
  4. /*
  5. 1、Collection中能存放什么元素?
  6. 未使用泛型之前,Collection可以存储Object类的所有子类型
  7. 使用泛型之后,Collection中只能存放某个具体的数据类型。
  8. 2、Collection的常用方法:add();clear();size();contain();remove();toCharArray();iterator();
  9. */
  10. public class ClooectionTest01 {
  11. public static void main(String[] args) {
  12. Collection c = new ArrayList(); //多态
  13. c.add("zs");
  14. c.add(125); //就算是这里,其实集合存储的还是引用
  15. c.add(new Student());
  16. c.add(129);
  17. System.out.println(c.size()); //3
  18. System.out.println(c.contains("zs"));//true
  19. System.out.println(c.remove(125));//true
  20. Object[] objects = c.toArray();
  21. for (int i = 0; i < objects.length; i++) {
  22. //zs javase.collection.Student@1b6d3586 129
  23. System.out.print(objects[i]+"\t");
  24. }
  25. }
  26. }
  27. class Student{
  28. }

重点常用方法(亟待加强)

iterator方法(其一)

  1. package javase.collection;
  2. import java.util.ArrayList;
  3. import java.util.Collection;
  4. import java.util.HashSet;
  5. import java.util.Iterator;
  6. /**
  7. * 迭代专题:Collection及其子实现能用,Map接口不行
  8. */
  9. public class CollectionTest02 {
  10. public static void main(String[] args) {
  11. Collection c = new ArrayList();
  12. c.add("zs");
  13. c.add("ls");
  14. c.add(123); //存进去是什么类型,取出来还是什么类型,只是输出时会转成字符串
  15. c.add(123);
  16. Iterator it = c.iterator();
  17. while (it.hasNext()){
  18. System.out.print(it.next()+"\t"); //zs ls 123 123
  19. }
  20. Collection c2 = new HashSet();
  21. c2.add(1);
  22. c2.add(20);
  23. c2.add(5);
  24. c2.add(99);
  25. c2.add(90);
  26. c2.add(99);
  27. Iterator it2 = c2.iterator();
  28. while (it2.hasNext()){
  29. //1 99 20 5 90,无序且不可重复
  30. System.out.print(it2.next()+"\t");
  31. }
  32. }
  33. }

iterator方法(其二)

  1. package javase.collection;
  2. import java.util.ArrayList;
  3. import java.util.Collection;
  4. import java.util.Iterator;
  5. /*
  6. 关于集合元素的remove方法
  7. 重点:当集合的结构发生改变时,迭代器必须重新获取,如果还是用以前老的迭代器,
  8. 会出现并发修改异常java.util.ConcurrentModificationException。
  9. 在迭代集合元素的过程中,不能调用集合对象的remove方法删除元素。
  10. */
  11. public class CollectionTest05 {
  12. public static void main(String[] args) {
  13. /*Collection c = new ArrayList();
  14. *//**
  15. * 注意:此时获取的迭代器,指向的是集合中没有元素状态下的迭代器
  16. * 集合结构一旦发生改变,迭代器必须重新获取
  17. *//*
  18. Iterator it = c.iterator();
  19. c.add(1);
  20. c.add(2);
  21. c.add(3);
  22. while (it.hasNext()){
  23. //Exception in thread "main" java.util.ConcurrentModificationException
  24. Object obj = it.next();
  25. System.out.print(obj+" ");
  26. }*/
  27. Collection c2 = new ArrayList();
  28. c2.add(999);
  29. c2.add("zs");
  30. c2.add(new Object());
  31. Iterator it2 = c2.iterator();
  32. while (it2.hasNext()){
  33. //未做任何修改之前输出:
  34. //999 zs java.lang.Object@1b6d3586
  35. Object obj2 = it2.next();
  36. //添加一行删除元素的代码后输出:
  37. //999 Exception in thread "main" java.util.ConcurrentModificationException
  38. // c2.remove(obj2); //直接通过集合去删除元素,没有通知迭代器,
  39. //导致快照和原集合状态不同,产生并发修改异常
  40. System.out.print(obj2+" ");
  41. }
  42. //使用迭代器可以删除,删除的一定是迭代器指向的当前元素
  43. it2.remove();
  44. System.out.println(c2.size()); //2
  45. }
  46. }

image.png
image.png

contains方法

  1. package javase.collection;
  2. import java.util.ArrayList;
  3. import java.util.Collection;
  4. //总结,放在Collection集合的类型一定要重写equals方法
  5. public class CollectionTest03 {
  6. public static void main(String[] args) {
  7. Collection c = new ArrayList();
  8. String s1 = new String("123");
  9. String s2 = new String("234");
  10. String s3 = new String("123");
  11. c.add(s1);
  12. c.add(s2);
  13. //之所以输出为true,是因为存储元素时会比较内容,
  14. // contains方法先调用indexof方法,然后indexof方法调用indexOfRange方法
  15. // 最后indexOfRange调用equals方法
  16. System.out.println(c.contains(s3)); //true
  17. Collection c2 = new ArrayList();
  18. User user1 = new User("zs");
  19. User user2 = new User("zs");
  20. c2.add(user1);
  21. //输出结果为false,因为User类没有重写equals方法,比较的是地址
  22. System.out.println(c2.contains(user2)); //false
  23. }
  24. }
  25. class User{
  26. private String name;
  27. public User() {
  28. }
  29. public User(String name) {
  30. this.name = name;
  31. }
  32. }

image.png

remove方法

  1. package javase.collection;
  2. import java.util.ArrayList;
  3. import java.util.Collection;
  4. //测试remove()方法有没有重写equals方法
  5. public class CollectionTest04 {
  6. public static void main(String[] args) {
  7. Collection c = new ArrayList();
  8. String s1 = new String("hello");
  9. String s2 = new String("hello");
  10. c.add(s1);
  11. System.out.println(c.contains(s2));//true
  12. System.out.println(c.remove(s2));//true
  13. System.out.println(c.size());//0
  14. }
  15. }

总结

加入Collection集合的对象需要重写equals方法,除非像String那种已经重写了equals()方法的就不必了,一般自定义的类都要重写。

List接口

自身特色方法

  1. package javase.list;
  2. import java.util.ArrayList;
  3. import java.util.Iterator;
  4. import java.util.List;
  5. /*
  6. 测试List接口的常用方法:
  7. 1、存储特点:有序可重复
  8. 2、自身特色方法:
  9. ①void add(int index, E element)
  10. 在列表的指定位置插入指定元素(可选操作)。
  11. ②E get(int index)
  12. 返回列表中指定位置的元素。
  13. ③int hashCode()
  14. 返回列表的哈希码值。
  15. ④int indexOf(Object o)
  16. 返回此列表中第一次出现的指定元素的索引;如果此列表不包含该元素,则返回 -1。
  17. ⑤E set(int index, E element)
  18. 用指定元素替换列表中指定位置的元素(可选操作)。
  19. */
  20. public class ListTest01 {
  21. public static void main(String[] args) {
  22. List myList = new ArrayList();
  23. myList.add("1");
  24. myList.add(2);
  25. myList.add("999");
  26. //向指定位置添加元素,用得不多
  27. myList.add(1,777);
  28. Iterator mle = myList.iterator();
  29. while (mle.hasNext()){
  30. Object obj = mle.next();
  31. //1 777 2 999
  32. System.out.print(obj+" ");
  33. }
  34. //获取下标元素
  35. Object obj2 = myList.get(1);
  36. System.out.println(obj2); //777
  37. //获取下标元素,改进
  38. for (int i = 0; i < myList.size(); i++) {
  39. //1 777 2 999
  40. System.out.print(myList.get(i)+" ");
  41. }
  42. //获取元素第一次、最后一次出现处的索引
  43. System.out.println(myList.indexOf(777)); //1
  44. System.out.println(myList.lastIndexOf(777)); //1
  45. //根据下标删除元素
  46. Object obj3 = myList.remove(1);
  47. System.out.println(myList.size()); //3
  48. //修改元素
  49. myList.set(1,"这就是中国");
  50. System.out.println(myList.get(1)); //这就是中国
  51. //查看整体
  52. for (int i = 0; i < myList.size(); i++) {
  53. //1 这就是中国 999
  54. System.out.print(myList.get(i)+" ");
  55. }
  56. }
  57. }

子实现之ArrayList

特点

  1. /**
  2. * Default initial capacity.
  3. */
  4. //初始容量是10
  5. //底层先创建一个长度为0的数组,当添加第一个元素的时候初始化容量为10
  6. private static final int DEFAULT_CAPACITY = 10;
  7. //底层是个Object类型的数组
  8. transient Object[] elementData;
  9. //扩容机制,是原来的1.5倍
  10. /**
  11. * Increases the capacity to ensure that it can hold at least the
  12. * number of elements specified by the minimum capacity argument.
  13. *
  14. * @param minCapacity the desired minimum capacity
  15. */
  16. private void grow(int minCapacity) {
  17. // overflow-conscious code
  18. int oldCapacity = elementData.length;
  19. int newCapacity = oldCapacity + (oldCapacity >> 1);
  20. if (newCapacity - minCapacity < 0)
  21. newCapacity = minCapacity;
  22. if (newCapacity - MAX_ARRAY_SIZE > 0)
  23. newCapacity = hugeCapacity(minCapacity);
  24. // minCapacity is usually close to size, so this is a win:
  25. elementData = Arrays.copyOf(elementData, newCapacity);
  26. }
  27. //带一个整型参数的构造器:可以自定义初始容量
  28. public ArrayList(int initialCapacity) {
  29. if (initialCapacity > 0) {
  30. this.elementData = new Object[initialCapacity];
  31. } else if (initialCapacity == 0) {
  32. this.elementData = EMPTY_ELEMENTDATA;
  33. } else {
  34. throw new IllegalArgumentException("Illegal Capacity: "+
  35. initialCapacity);
  36. }
  37. }
  38. //带一个集合类型参数的构造器
  39. /**
  40. * Constructs a list containing the elements of the specified
  41. * collection, in the order they are returned by the collection's
  42. * iterator.
  43. *
  44. * @param c the collection whose elements are to be placed into this list
  45. * @throws NullPointerException if the specified collection is null
  46. */
  47. public ArrayList(Collection<? extends E> c) {
  48. elementData = c.toArray();
  49. if ((size = elementData.length) != 0) {
  50. // c.toArray might (incorrectly) not return Object[] (see 6260652)
  51. if (elementData.getClass() != Object[].class)
  52. elementData = Arrays.copyOf(elementData, size, Object[].class);
  53. } else {
  54. // replace with empty array.
  55. this.elementData = EMPTY_ELEMENTDATA;
  56. }
  57. }
  58. //无参构造器
  59. /**
  60. * Constructs an empty list with an initial capacity of ten.
  61. */
  62. public ArrayList() {
  63. this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
  64. }

ArrayList集合类3个构造方法

  1. package javase.list.arraylist;
  2. import java.util.ArrayList;
  3. import java.util.HashSet;
  4. import java.util.List;
  5. import java.util.Set;
  6. public class ArrayListTest01 {
  7. public static void main(String[] args) {
  8. //默认数组初始容量为10
  9. List c1 = new ArrayList();
  10. //指定数组初始容量为100
  11. List c2 = new ArrayList(100);
  12. //将一个集合作为参数传到ArrayList集合类中
  13. Set set = new HashSet();
  14. set.add(11);
  15. set.add(22);
  16. set.add(33);
  17. set.add(44);
  18. List c3 = new ArrayList(set);
  19. for (int i = 0; i < set.size(); i++) {
  20. System.out.print(c3.get(i)+" "); //33 22 11 44
  21. }
  22. }
  23. }

子实现之LinkedList

  1. package javase.list;
  2. import java.util.LinkedList;
  3. import java.util.List;
  4. /**
  5. * 链表的优点:
  6. * 由于链表上的元素在空间存储上内存地址不连续
  7. * 所以随机增删的时候不会有大量元素位移,因此随机增删效率较高
  8. * 链表的缺点:
  9. * 不能通过数学表达式计算被查找元素的内存地址,每一次查找都是从头开始遍历,
  10. * 直到找到为止,所以LinkedList集合检索效率较低
  11. *
  12. * ArrayList:把检索发挥到极致
  13. * LinkedList:本质上是双向链表,把随机增删发挥到极致
  14. * 由于添加元素都是往末尾添加的,而ArrayList末尾添加元素效率还是很高的,
  15. * 所以ArrayList用得比LinkedList多
  16. */
  17. public class LinkedListTest01 {
  18. public static void main(String[] args) {
  19. List c = new LinkedList();
  20. c.add(100);
  21. c.add(200);
  22. c.add(300);
  23. c.add(400);
  24. for (int i = 0; i < c.size(); i++) {
  25. //100 200 300 400
  26. System.out.print(c.get(i)+" ");
  27. /*
  28. 同ArrayList一样,LinkedList也是有下标的
  29. 注意:ArrayList之所以检索效率比较高,不是单纯因为下标的原因,而是因为底层数组发挥了作用
  30. LinkedList集合照样有下标,只是检索某个元素的时候效率比较低,因为只能从头结点开始一个一个遍历
  31. */
  32. }
  33. }
  34. }

子实现之Vector

  1. //初始容量是10
  2. /**
  3. * Constructs an empty vector so that its internal data array
  4. * has size {@code 10} and its standard capacity increment is
  5. * zero.
  6. */
  7. public Vector() {
  8. this(10);
  9. }
  10. //扩容机制是原来的2倍
  11. /**
  12. * The amount by which the capacity of the vector is automatically
  13. * incremented when its size becomes greater than its capacity. If
  14. * the capacity increment is less than or equal to zero, the capacity
  15. * of the vector is doubled each time it needs to grow.
  16. *
  17. * @serial
  18. */
  19. protected int capacityIncrement;
  20. //随便列举一个方法,证明Vector是线程安全的
  21. /**
  22. * Returns the number of components in this vector.
  23. *
  24. * @return the number of components in this vector
  25. */
  26. public synchronized int size() {
  27. return elementCount;
  28. }

如何将ArrayList类转成线程安全的?

  1. package javase.list;
  2. import java.util.ArrayList;
  3. import java.util.Collections;
  4. import java.util.List;
  5. public class ListTest02 {
  6. public static void main(String[] args) {
  7. //非线程安全的
  8. List myList = new ArrayList();
  9. //如何将其变成线程安全的?这行的代码
  10. Collections.synchronizedList(myList);
  11. myList.add(10);
  12. myList.add(20);
  13. myList.add(30);
  14. }
  15. }

泛型

普通泛型

  1. package javase.generic;
  2. import java.util.ArrayList;
  3. import java.util.Iterator;
  4. import java.util.List;
  5. /*jdk5.0推出的泛型机制
  6. 1、泛型这种语法机制,只在程序编译阶段起作用,只是给编译器参考的,运行阶段泛型没用
  7. 2、优点:
  8. ①集合存储的元素统一了
  9. ②从集合中取出的元素类型是泛型指定的类型,不需要进行大量的向下转型
  10. 3、缺点:
  11. ①导致集合中存储的元素缺乏多样性
  12. ②大多数业务中,集合中的元素还是统一的,所以泛型的这种特性还是被大家认可的
  13. */
  14. public class GenericTest01 {
  15. public static void main(String[] args) {
  16. /*List myList = new ArrayList();
  17. Cat c = new Cat();
  18. Bird b = new Bird();
  19. myList.add(c);
  20. myList.add(b);
  21. Iterator it = myList.iterator();
  22. //遍历集合,取出每个Animal,让它move
  23. while (it.hasNext()){
  24. Object obj = it.next();
  25. if (obj instanceof Animal){
  26. Animal a = (Animal) obj;
  27. a.move();
  28. }
  29. }
  30. }*/
  31. //使用泛型List<Animal>之后,表示List集合中只允许存储Animal类型的数据
  32. //用泛型来指定集合中存储的数据元素
  33. List<Animal> myList2 = new ArrayList<>();
  34. //指定List集合中只能存储Animal,那么,存储String就编译报错了
  35. //这样使用泛型之后,集合中元素的数据类型就更加统一了
  36. Cat c = new Cat();
  37. Bird b = new Bird();
  38. myList2.add(c);
  39. myList2.add(b);
  40. //获取迭代器,这个表示迭代器迭代的是Animal类型
  41. Iterator<Animal> it2 = myList2.iterator();
  42. while (it2.hasNext()){
  43. //使用泛型之后,每一次迭代返回的都是Animal类型
  44. Animal a2 = it2.next();
  45. //这里不需要进行强制类型转换了,直接调用
  46. //但是调用子类特有的方法还是需要向下转型的
  47. a2.move();
  48. }
  49. }
  50. }
  51. class Animal{
  52. public void move(){
  53. System.out.println("动物在移动");
  54. }
  55. }
  56. class Cat extends Animal{
  57. public void catchMouse(){
  58. System.out.println("猫抓老鼠");
  59. }
  60. }
  61. class Bird extends Animal{
  62. public void fly(){
  63. System.out.println("鸟儿在飞翔");
  64. }
  65. }
  66. //2种方式都输出:
  67. // 动物在移动
  68. //动物在移动

自定义泛型

  1. package javase.generic;
  2. //自定义泛型
  3. //定义时先是以某种格式,而后使用时是指定类型格式
  4. public class GenericTest03<MyType> {
  5. public void doSome(MyType mt){
  6. System.out.println(mt);
  7. }
  8. public static void main(String[] args) {
  9. GenericTest03<String> gt = new GenericTest03<>();
  10. //类型不匹配
  11. // gt.doSome(100);
  12. //类型匹配
  13. gt.doSome("abc"); //abc
  14. }
  15. }

增强for循环

定义

  1. package javase.forcycle;
  2. public class StrongerForCycleTest01 {
  3. public static void main(String[] args) {
  4. int arr[] = {1,2,3,4,5,232,43,434,2135,65};
  5. //普通for循环
  6. for (int i = 0; i < arr.length; i++) {
  7. System.out.print(arr[i]+" ");
  8. }
  9. //增强for循环(语法格式如下:)
  10. /*
  11. for(元素类型 变量名:数组或集合){
  12. 变量名.sout;
  13. }
  14. */
  15. System.out.println("\n"+"===================================");
  16. //i就是数组中的每一个元素,foreach缺点是没有下标,
  17. //这里建议没有下标时采用增强for循环
  18. //同时建议采用:arr.iter格式快速生成增强for循环语法格式
  19. for (int i : arr) {
  20. System.out.print(i+" ");
  21. }
  22. }
  23. }
  24. /*
  25. 输出如下:
  26. 1 2 3 4 5 232 43 434 2135 65
  27. ===================================
  28. 1 2 3 4 5 232 43 434 2135 65
  29. */

集合三类循环的比较

  1. package javase.forcycle;
  2. import java.util.ArrayList;
  3. import java.util.Iterator;
  4. import java.util.List;
  5. //三种方式遍历集合
  6. public class StrongerForCycleTest02 {
  7. public static void main(String[] args) {
  8. List<String> mylist = new ArrayList<>();
  9. mylist.add("my test01");
  10. mylist.add("my test02");
  11. mylist.add("my test03");
  12. //方式一:迭代器遍历
  13. Iterator<String> it1 = mylist.iterator();
  14. while (it1.hasNext()){
  15. String s1 = it1.next();
  16. System.out.print(s1+" ");
  17. }
  18. System.out.println();
  19. //方式二:普通for循环
  20. for (int i = 0; i < mylist.size(); i++) {
  21. System.out.print(mylist.get(i)+" ");
  22. }
  23. System.out.println();
  24. //方式三:增强for循环
  25. for (String s : mylist) {
  26. System.out.print(s+" ");
  27. }
  28. }
  29. }
  30. /*
  31. 结果输出:
  32. my test01 my test02 my test03
  33. my test01 my test02 my test03
  34. my test01 my test02 my test03
  35. */

Set接口

HashSet集合类

HashSet类效果演示

  1. package javase.hashset;
  2. import java.util.HashSet;
  3. import java.util.Set;
  4. public class HashSetTest01 {
  5. public static void main(String[] args) {
  6. Set<String> strs = new HashSet<>();
  7. strs.add("hello1");
  8. strs.add("hello2");
  9. strs.add("hello3");
  10. strs.add("hello4");
  11. strs.add("hello5");
  12. strs.add("hello1");
  13. strs.add("hello1");
  14. for (String str : strs) {
  15. //hello1 hello4 hello5 hello2 hello3
  16. System.out.print(str+" ");
  17. //存储时顺序和取出的顺序不同,不可重复,
  18. // 且放到HashSet集合中的元素实际上是放到HashMap集合的key部分了
  19. }
  20. }
  21. }

TreeSet集合类

TreeSet类效果演示

  1. package javase.set.treeset;
  2. import java.util.Set;
  3. import java.util.TreeSet;
  4. //验证TreeSet的特性:①无序 ②不可重复
  5. //但存储的元素可以自动按照大小排序,称为可排序集合
  6. //这里的无序是指存入和取出的顺序不一样,没有下标
  7. public class TreeSetTest01 {
  8. public static void main(String[] args) {
  9. Set<String> strs = new TreeSet<>();
  10. strs.add("A");
  11. strs.add("B");
  12. strs.add("Z");
  13. strs.add("X");
  14. strs.add("Y");
  15. strs.add("Z");
  16. for (String str : strs) {
  17. //从小到大自动排序
  18. System.out.print(str+" ");//A B X Y Z
  19. }
  20. }
  21. }

TreeSet无法对自定义类型进行排序★★

  1. package javase.set.treeset;
  2. import java.util.TreeSet;
  3. /*
  4. 1、TreeSet集合底层实际上是一个TreeMap
  5. 2、TreeMap集合底层是一个二叉树
  6. 3、放到TreeSet集合中的元素,等同于放到TreeMap集合key部分
  7. 4、TreeSet集合中的元素:无序不可重复,但是可以按照元素的大小顺序自动排序,被称为:可排序集合
  8. */
  9. public class TreeSetTest02 {
  10. //对于自定义的类型来说,TreeSet不可以排序
  11. public static void main(String[] args) {
  12. Person p1 = new Person(20);
  13. Person p2 = new Person(30);
  14. Person p3 = new Person(25);
  15. Person p4 = new Person(35);
  16. TreeSet<Person> persons = new TreeSet<>();
  17. persons.add(p1);
  18. persons.add(p2);
  19. persons.add(p3);
  20. persons.add(p4);
  21. for (Person person : persons) {
  22. System.out.println(person.toString());
  23. }
  24. }
  25. }
  26. class Person{
  27. int age;
  28. @Override
  29. public String toString() {
  30. return "Person{" +
  31. "age=" + age +
  32. '}';
  33. }
  34. public Person(int age) {
  35. this.age = age;
  36. }
  37. }

运行结果如下图:
image.png

探究原因

要在TreeMap的put方法中去找:

  1. /**
  2. * Constructs a new, empty tree set, sorted according to the
  3. * natural ordering of its elements. All elements inserted into
  4. * the set must implement the {@link Comparable} interface.
  5. * Furthermore, all such elements must be <i>mutually
  6. * comparable</i>: {@code e1.compareTo(e2)} must not throw a
  7. * {@code ClassCastException} for any elements {@code e1} and
  8. * {@code e2} in the set. If the user attempts to add an element
  9. * to the set that violates this constraint (for example, the user
  10. * attempts to add a string element to a set whose elements are
  11. * integers), the {@code add} call will throw a
  12. * {@code ClassCastException}.
  13. */
  14. public TreeSet() {
  15. this(new TreeMap<E,Object>());
  16. }
  17. /**
  18. * Constructs a new, empty tree map, using the natural ordering of its
  19. * keys. All keys inserted into the map must implement the {@link
  20. * Comparable} interface. Furthermore, all such keys must be
  21. * <em>mutually comparable</em>: {@code k1.compareTo(k2)} must not throw
  22. * a {@code ClassCastException} for any keys {@code k1} and
  23. * {@code k2} in the map. If the user attempts to put a key into the
  24. * map that violates this constraint (for example, the user attempts to
  25. * put a string key into a map whose keys are integers), the
  26. * {@code put(Object key, Object value)} call will throw a
  27. * {@code ClassCastException}.
  28. */
  29. public TreeMap() {
  30. comparator = null;
  31. }
  32. /**
  33. * Adds the specified element to this set if it is not already present.
  34. * More formally, adds the specified element {@code e} to this set if
  35. * the set contains no element {@code e2} such that
  36. * <tt>(e==null&nbsp;?&nbsp;e2==null&nbsp;:&nbsp;e.equals(e2))</tt>.
  37. * If this set already contains the element, the call leaves the set
  38. * unchanged and returns {@code false}.
  39. *
  40. * @param e element to be added to this set
  41. * @return {@code true} if this set did not already contain the specified
  42. * element
  43. * @throws ClassCastException if the specified object cannot be compared
  44. * with the elements currently in this set
  45. * @throws NullPointerException if the specified element is null
  46. * and this set uses natural ordering, or its comparator
  47. * does not permit null elements
  48. */
  49. public boolean add(E e) {
  50. return m.put(e, PRESENT)==null;
  51. }
  52. /**
  53. * Associates the specified value with the specified key in this map.
  54. * If the map previously contained a mapping for the key, the old
  55. * value is replaced.
  56. *
  57. * @param key key with which the specified value is to be associated
  58. * @param value value to be associated with the specified key
  59. *
  60. * @return the previous value associated with {@code key}, or
  61. * {@code null} if there was no mapping for {@code key}.
  62. * (A {@code null} return can also indicate that the map
  63. * previously associated {@code null} with {@code key}.)
  64. * @throws ClassCastException if the specified key cannot be compared
  65. * with the keys currently in the map
  66. * @throws NullPointerException if the specified key is null
  67. * and this map uses natural ordering, or its comparator
  68. * does not permit null keys
  69. */
  70. public V put(K key, V value) {
  71. Entry<K,V> t = root;
  72. if (t == null) {
  73. compare(key, key); // type (and possibly null) check
  74. root = new Entry<>(key, value, null);
  75. size = 1;
  76. modCount++;
  77. return null;
  78. }
  79. int cmp;
  80. Entry<K,V> parent;
  81. // split comparator and comparable paths
  82. Comparator<? super K> cpr = comparator;
  83. if (cpr != null) {
  84. do {
  85. parent = t;
  86. cmp = cpr.compare(key, t.key);
  87. if (cmp < 0)
  88. t = t.left;
  89. else if (cmp > 0)
  90. t = t.right;
  91. else
  92. return t.setValue(value);
  93. } while (t != null);
  94. }
  95. else {
  96. if (key == null)
  97. throw new NullPointerException();
  98. @SuppressWarnings("unchecked")
  99. //这行就极有可能出现问题
  100. //给人的启示就是,要实现Comparable接口,才能比较大小,才好转型,不会出现类型转换异常
  101. //怎么做?实现Comparable接口,重写compareTo()方法,自定义规则
  102. Comparable<? super K> k = (Comparable<? super K>) key;
  103. do {
  104. parent = t;
  105. cmp = k.compareTo(t.key);
  106. if (cmp < 0)
  107. t = t.left;
  108. else if (cmp > 0)
  109. t = t.right;
  110. else
  111. return t.setValue(value);
  112. } while (t != null);
  113. }
  114. Entry<K,V> e = new Entry<>(key, value, parent);
  115. if (cmp < 0)
  116. parent.left = e;
  117. else
  118. parent.right = e;
  119. fixAfterInsertion(e);
  120. size++;
  121. modCount++;
  122. return null;
  123. }

解决措施

1.bean类实现Comparable接口,重写compareTo()方法

  1. package javase.set.treeset;
  2. import java.util.TreeSet;
  3. //先按照年龄升序,如果年龄一样再按照姓名升序
  4. public class TreeSetTest05 {
  5. public static void main(String[] args) {
  6. TreeSet<Vip> vips = new TreeSet<>();
  7. vips.add(new Vip("zs",20));
  8. vips.add(new Vip("ls",30));
  9. vips.add(new Vip("ww",60));
  10. vips.add(new Vip("zl",27));
  11. for (Vip vip : vips) {
  12. //Vip{name='zs', age=20} Vip{name='zl', age=27} Vip{name='ls', age=30} Vip{name='ww', age=60}
  13. System.out.print(vip+" ");
  14. }
  15. }
  16. }
  17. class Vip implements Comparable<Vip>{
  18. String name;
  19. int age;
  20. public Vip(String name, int age) {
  21. this.name = name;
  22. this.age = age;
  23. }
  24. @Override
  25. public String toString() {
  26. return "Vip{" +
  27. "name='" + name + '\'' +
  28. ", age=" + age +
  29. '}';
  30. }
  31. @Override
  32. public int compareTo(Vip v) {
  33. if (this.age == v.age){
  34. //姓名是String类型,可以直接比,调用compareTo()来完成
  35. return this.name.compareTo(v.name);
  36. }else {
  37. return this.age - v.age;
  38. }
  39. }
  40. }

2.使用比较器的方式

  1. package javase.set.treeset;
  2. import java.util.Comparator;
  3. import java.util.TreeSet;
  4. /**
  5. * 放到TreeSet或者TreeMap集合key部分的元素想要做到排序,包括以下2种方式:
  6. * 第一种:放在集合中的元素实现java.lang.Comparable接口
  7. * 第二种:在构造TreeSet或者TreeMap集合的时候给它传一个比较器对象
  8. * Comparable和Comparator怎么选择呢?
  9. * 当比较规则不会发生改变的时候,或者说当比较规则只有1个的时候,建议实现Comparable接口
  10. * 当比较规则有多个,并且需要多个比较规则之间频繁切换,建议使用Comparator接口
  11. * Comparator接口的设计符合OCP原则
  12. */
  13. public class TreeSetTest06 {
  14. public static void main(String[] args) {
  15. //创建TreeSet集合的时候,需要使用这个比较器
  16. //注意:这样是不行的,没有通过构造器传递一个比较器进去
  17. // TreeSet<Wugui> wuguis = new TreeSet<>();
  18. TreeSet<Wugui> wuguis = new TreeSet<>(new WuguiComparator());
  19. wuguis.add(new Wugui(1000));
  20. wuguis.add(new Wugui(800));
  21. wuguis.add(new Wugui(789));
  22. wuguis.add(new Wugui(606));
  23. for (Wugui wugui : wuguis) {
  24. //小乌龟【age=606】 小乌龟【age=789】 小乌龟【age=800】 小乌龟【age=1000】
  25. System.out.print(wugui+" ");
  26. }
  27. }
  28. }
  29. class Wugui{
  30. int age;
  31. public Wugui(int age) {
  32. this.age = age;
  33. }
  34. @Override
  35. public String toString() {
  36. return "小乌龟【" +
  37. "age=" + age +
  38. '】';
  39. }
  40. }
  41. //单独在这里编写一个比较器
  42. //比较器实现java.util.Comparator接口(Comparable是java.lang包下的,Comparator是java.util包下的)
  43. class WuguiComparator implements Comparator<Wugui>{
  44. @Override
  45. public int compare(Wugui o1, Wugui o2) {
  46. //指定比较规则:按年龄排序
  47. return o1.age - o2.age;
  48. }
  49. }

Map接口

Map接口的普通常用方法

  1. package javase.map;
  2. import java.util.Collection;
  3. import java.util.HashMap;
  4. import java.util.Map;
  5. /*
  6. java.util.Map接口中常用的方法:
  7. 1、Map集合以key和value的方式存储数据:键值对
  8. key和value是引用数据类型;
  9. key和value都是存储对象的内存地址
  10. key起到主导的地位,value是key的一个附属品
  11. 2、Map接口常用方法:
  12. void clear() 清空Map集合
  13. boolean containsKey(Object key) 判断集合是否包含某个key
  14. boolean containsValue(Object value) 判断集合是否包含某个value
  15. V get(Object key) 通过key获取对应的value值
  16. boolean isEmpty() 判断集合是否为空
  17. Set<K> keySet() 获取所有的key(所有的key组成自己的set集合)
  18. V put(K key, V value) 添加一组键值对
  19. V remove(Object key) 根据key删除所在的键值对
  20. int size() 获取Map集合中元素的个数
  21. Collection<V> values() 获取所有的value,返回一个Collection
  22. Set<Map.Entry<K,V>> entrySet() 将Map集合转成Set集合
  23. 假设现在有一个Map集合,如下所示:
  24. map1集合对象:
  25. key value
  26. -----------------------
  27. 1 zhangsan
  28. 2 lisi
  29. 3 wangwu
  30. 4 zhaoliu
  31. Set set = map1.entrySet();
  32. set集合对象:
  33. 1=zhangsan【注意:Map集合通过entrySet()方法转换成的这个Set集合,Set集合中元素的类型是Map.EntrySet】
  34. 2=lisi 【Map.Entry和String一样,都是一种类型的名字,只不过Map.Entry是静态内部类,是Map中的】
  35. 3=wangwu
  36. 4=zhaoliu
  37. */
  38. public class MapTest01 {
  39. public static void main(String[] args) {
  40. Map<Integer,String> map = new HashMap<>();
  41. map.put(1,"zs");
  42. map.put(2,"ls");
  43. map.put(3,"ww");
  44. map.put(4,"zl");
  45. //V get(Object key) 通过key获取对应的value值
  46. String val = map.get(2);
  47. System.out.println(val); //ls
  48. //int size() 获取Map集合中元素的个数
  49. System.out.println("键值对的数量" + map.size());//键值对的数量4
  50. //V remove(Object key) 根据key删除所在的键值对
  51. map.remove(1);
  52. System.out.println("键值对的数量" + map.size());//键值对的数量3
  53. //boolean containsKey(Object key) 判断集合是否包含某个key
  54. //contains方法底层调用的都是equals进行比对的,所以自定义的类型需要重写equals方法
  55. System.out.println(map.containsKey(new Integer(2)));//true
  56. //boolean containsValue(Object value) 判断集合是否包含某个value
  57. System.out.println(map.containsValue("zs"));//false
  58. //Collection<V> values() 获取所有的value,返回一个Collection
  59. Collection<String> values = map.values();
  60. for (String value : values) {
  61. System.out.print(" "+value);//ls ww zl
  62. }
  63. //void clear() 清空Map集合
  64. map.clear();
  65. System.out.println("键值对的数量" + map.size());//键值对的数量0
  66. //boolean isEmpty() 判断集合是否为空
  67. System.out.println(map.isEmpty());//true
  68. }
  69. }

Map接口的遍历三种方式

  1. package javase.map;
  2. import java.util.HashMap;
  3. import java.util.Iterator;
  4. import java.util.Map;
  5. import java.util.Set;
  6. //Map集合的遍历
  7. public class MapTest02 {
  8. public static void main(String[] args) {
  9. Map<Integer,String> map = new HashMap<>();
  10. map.put(1,"zs");
  11. map.put(2,"ls");
  12. map.put(3,"ww");
  13. map.put(4,"zl");
  14. Set<Integer> keys = map.keySet();
  15. //前2种方式遍历:迭代器和foreach
  16. Iterator<Integer> it = keys.iterator();
  17. while (it.hasNext()){
  18. //1:zs 2:ls 3:ww 4:zl
  19. Integer key = it.next();
  20. String value = map.get(key);
  21. System.out.print(key+":"+value+" ");
  22. }
  23. System.out.println();
  24. for (Integer key : keys) {
  25. //1:zs 2:ls 3:ww 4:zl
  26. System.out.print(key+":"+map.get(key)+" ");
  27. }
  28. System.out.println();
  29. //第三种方式遍历:Set集合中元素的类型是:Map.Entry
  30. // Set<Map.Entry<K,V>> entrySet() 将Map集合转成Set集合
  31. Set<Map.Entry<Integer, String>> set = map.entrySet();
  32. //遍历Set集合,每次取出一个Node
  33. //这种效率其实更高,因为key和value都是直接从node对象获取的
  34. Iterator<Map.Entry<Integer, String>> it2 = set.iterator();
  35. while (it2.hasNext()){
  36. Map.Entry<Integer, String> node = it2.next();
  37. Integer key = node.getKey();
  38. String value = node.getValue();
  39. //1:zs 2:ls 3:ww 4:zl
  40. System.out.print(key + ":" + value+"\t");
  41. }
  42. System.out.println();
  43. for (Map.Entry<Integer, String> node : set) {
  44. //1-->zs 2-->ls 3-->ww 4-->zl
  45. System.out.print(node.getKey() + "-->" + node.getValue()+"\t");
  46. }
  47. }
  48. }

image.png

HashMap集合类★★

重点方法:put和get

image.png

hashCode()方法和equals()方法重写的问题

  1. package javase.hashmap.bean;
  2. public class Student {
  3. private String name;
  4. public String getName() {
  5. return name;
  6. }
  7. public void setName(String name) {
  8. this.name = name;
  9. }
  10. public Student(String name) {
  11. this.name = name;
  12. }
  13. public Student() {
  14. }
  15. //重写equals
  16. public boolean equals(Object obj){
  17. if (obj == null||!(obj instanceof Student)) return false;
  18. if (obj == this) return true;
  19. Student s = (Student) obj;
  20. if (this.name.equals(s.name)) return true;
  21. return false;
  22. }
  23. }
  1. package javase.hashmap;
  2. import javase.hashmap.bean.Student;
  3. import java.util.HashSet;
  4. /*
  5. 1、向Map集合中存,以及从Map集合中取,都是先调用key的hashCode方法,然后再调用equals方法。
  6. equals方法有可能调,也可能不调。
  7. 2、拿put(k,v)和get(k)举例,什么时候不会调用?
  8. k.hashCode()方法返回哈希值,哈希值经过哈希算法转换成数组下标;如果数组下标为null,
  9. equals不需要执行。
  10. 3、hashCode方法和equals方法不用研究了,直接用IDEA工具生成,但这两个方法需要同时生成
  11. 4、终极结论:放在HashMap集合key部分的,以及放在HashSet集合中的元素,
  12. 需要同时重写hashCode方法和equals方法
  13. */
  14. public class HashMapTest02 {
  15. public static void main(String[] args) {
  16. Student s1 = new Student("zs");
  17. Student s2 = new Student("zs");
  18. //重写equals方法之前是false,重写之后是true
  19. System.out.println(s1.equals(s2));
  20. System.out.println(s1.hashCode());
  21. System.out.println(s2.hashCode());
  22. //s1.equals(s2)结果已经是true了,表示s1和s2是一样的,同样地,那么往HashSet集合的话,
  23. //按说只能放进去一个
  24. HashSet<Student> students = new HashSet<>();
  25. students.add(s1);
  26. students.add(s2);
  27. //按理说是1,实际上是2。所以一个类的equals方法重写了,那么hashCode()方法也必须重写
  28. //并且equals方法返回如果是true,hashCode()方法返回的值必须一样
  29. System.out.println(students.size());
  30. }
  31. }

JDK8中HashMap新特性

  1. HashMap集合底层是哈希表,这种数据结构是非线程安全的。JDK8以后,如果哈希表单向链表中元素超过8个,单向链表这种数据结构会变成红黑树。当红黑树的节点数量小于6时,会重新把红黑树变成单向链表数据结构。这种方式也是为了提高检索效率,二叉树的检索会再次缩小扫描范围,提高效率。源码如下:
  1. /**
  2. * The bin count threshold for using a tree rather than list for a
  3. * bin. Bins are converted to trees when adding an element to a
  4. * bin with at least this many nodes. The value must be greater
  5. * than 2 and should be at least 8 to mesh with assumptions in
  6. * tree removal about conversion back to plain bins upon
  7. * shrinkage.
  8. */
  9. static final int TREEIFY_THRESHOLD = 8;
  10. /**
  11. * The bin count threshold for untreeifying a (split) bin during a
  12. * resize operation. Should be less than TREEIFY_THRESHOLD, and at
  13. * most 6 to mesh with shrinkage detection under removal.
  14. */
  15. static final int UNTREEIFY_THRESHOLD = 6;
  16. /**
  17. * The smallest table capacity for which bins may be treeified.
  18. * (Otherwise the table is resized if too many nodes in a bin.)
  19. * Should be at least 4 * TREEIFY_THRESHOLD to avoid conflicts
  20. * between resizing and treeification thresholds.
  21. */
  22. static final int MIN_TREEIFY_CAPACITY = 64;

HashMap集合类存放空值元素的问题

  1. package javase.hashmap;
  2. import java.util.HashMap;
  3. //HashMap集合key部分允许为null吗?允许,但只能有1个
  4. public class HashMapTest03 {
  5. public static void main(String[] args) {
  6. HashMap map = new HashMap();
  7. map.put(null,null);
  8. System.out.println(map.size()); //1
  9. System.out.println(map.get(null));//null
  10. }
  11. }

HashTable集合类

HashTable类的基本信息

初始容量、负载因子、扩容机制

  1. /**
  2. * Constructs a new, empty hashtable with a default initial capacity (11)
  3. * and load factor (0.75).
  4. */
  5. //初始容量为11,负载因子为0.75
  6. public Hashtable() {
  7. this(11, 0.75f);
  8. }
  9. /**
  10. * Increases the capacity of and internally reorganizes this
  11. * hashtable, in order to accommodate and access its entries more
  12. * efficiently. This method is called automatically when the
  13. * number of keys in the hashtable exceeds this hashtable's capacity
  14. * and load factor.
  15. */
  16. //扩容机制是原来的2倍+1
  17. @SuppressWarnings("unchecked")
  18. protected void rehash() {
  19. int oldCapacity = table.length;
  20. Entry<?,?>[] oldMap = table;
  21. // overflow-conscious code
  22. int newCapacity = (oldCapacity << 1) + 1;
  23. if (newCapacity - MAX_ARRAY_SIZE > 0) {
  24. if (oldCapacity == MAX_ARRAY_SIZE)
  25. // Keep running with MAX_ARRAY_SIZE buckets
  26. return;
  27. newCapacity = MAX_ARRAY_SIZE;
  28. }
  29. Entry<?,?>[] newMap = new Entry<?,?>[newCapacity];
  30. modCount++;
  31. threshold = (int)Math.min(newCapacity * loadFactor, MAX_ARRAY_SIZE + 1);
  32. table = newMap;
  33. for (int i = oldCapacity ; i-- > 0 ;) {
  34. for (Entry<K,V> old = (Entry<K,V>)oldMap[i] ; old != null ; ) {
  35. Entry<K,V> e = old;
  36. old = old.next;
  37. int index = (e.hash & 0x7FFFFFFF) % newCapacity;
  38. e.next = (Entry<K,V>)newMap[index];
  39. newMap[index] = e;
  40. }
  41. }
  42. }

HashTable集合类存放空值元素的问题

  1. package javase.hashtable;
  2. import java.util.Hashtable;
  3. //HashTable类的k-v都不能为空,否则空指针异常
  4. //HashTable方法都带有Synchronized关键字,因此是线程安全的,线程安全有其他解决方案,
  5. //因此HashTable对线程的处理导致效率较低,使用较少
  6. //
  7. public class HashTableTest01 {
  8. public static void main(String[] args) {
  9. Hashtable<Integer, String> ht = new Hashtable<>();
  10. ht.put(null,"zs"); //本行代码出现空指针异常
  11. ht.put(12,null); //本行代码也出现了空指针异常
  12. System.out.println(ht);
  13. }
  14. }

究其原因,是源代码有以下规则:

  1. /**
  2. * Maps the specified <code>key</code> to the specified
  3. * <code>value</code> in this hashtable. Neither the key nor the
  4. * value can be <code>null</code>. <p>
  5. *
  6. * The value can be retrieved by calling the <code>get</code> method
  7. * with a key that is equal to the original key.
  8. *
  9. * @param key the hashtable key
  10. * @param value the value
  11. * @return the previous value of the specified key in this hashtable,
  12. * or <code>null</code> if it did not have one
  13. * @exception NullPointerException if the key or value is
  14. * <code>null</code>
  15. * @see Object#equals(Object)
  16. * @see #get(Object)
  17. */
  18. public synchronized V put(K key, V value) {
  19. // Make sure the value is not null
  20. if (value == null) {
  21. throw new NullPointerException();
  22. }
  23. // Makes sure the key is not already in the hashtable.
  24. Entry<?,?> tab[] = table;
  25. int hash = key.hashCode();
  26. int index = (hash & 0x7FFFFFFF) % tab.length;
  27. @SuppressWarnings("unchecked")
  28. Entry<K,V> entry = (Entry<K,V>)tab[index];
  29. for(; entry != null ; entry = entry.next) {
  30. if ((entry.hash == hash) && entry.key.equals(key)) {
  31. V old = entry.value;
  32. entry.value = value;
  33. return old;
  34. }
  35. }
  36. addEntry(hash, key, value, index);
  37. return null;
  38. }

Properties类

  1. package javase.hashtable.properties;
  2. /*
  3. Properties是一个Map集合,继承自Hashtable,Properties的key和value
  4. 都是String类型,Properties被称为属性类对象
  5. */
  6. import java.util.Properties;
  7. /**
  8. * Properties类的常用方法:
  9. * String getProperty(String key) 用指定的键在此属性列表中搜索属性。
  10. * String getProperty(String key, String defaultValue) 用指定的键在属性列表中搜索属性。
  11. * void load(InputStream inStream) 从输入流中读取属性列表(键和元素对)。
  12. * void load(Reader reader) 按简单的面向行的格式从输入字符流中读取属性列表(键和元素对)。
  13. * Object setProperty(String key, String value) 调用 Hashtable 的方法 put。
  14. */
  15. public class PropertiesTest01 {
  16. public static void main(String[] args) {
  17. //需要掌握Properties的2个方法,一个存,一个取
  18. Properties pro = new Properties();
  19. pro.setProperty("username","root");
  20. pro.setProperty("password","admin123");
  21. String username = pro.getProperty("username");
  22. System.out.println(username); //root
  23. }
  24. }

Collections工具类常用方法

  1. package javase;
  2. import java.util.ArrayList;
  3. import java.util.Collections;
  4. //java.util.Collection 集合接口
  5. //java.util.Collections 集合工具类,方便集合的操作
  6. public class CollectionsTest {
  7. public static void main(String[] args) {
  8. //ArrayList集合不是线程安全的
  9. ArrayList<String> list = new ArrayList<>();
  10. //变成线程安全的
  11. Collections.synchronizedList(list);
  12. //排序
  13. //注意:对List集合中元素排序(限list),需要保证List集合中的元素实现了Comparable接口
  14. list.add("abc");
  15. list.add("abf");
  16. list.add("ahj");
  17. list.add("bcx");
  18. Collections.sort(list);
  19. for (String s : list) {
  20. //abc abf ahj bcx
  21. System.out.print(s+" ");
  22. }
  23. }
  24. }