02

    科技2022-08-01  105

    1 数组与集合

    1.1 集合与数组存储数据概述

    集合、数组都是对多个数据进行存储操作的结构,简称Java容器。

    说明:此时的存储,主要是指的是内存层面的存储,不涉及到持久化的存储。

    1.2数组存储特点

    一旦初始化后,其长度就确定了。

    数组一旦定义好,其元素类型也就确定了。我们只能操作指定类型的数据。

    String[] arr = new String[4];//数组类型和长度确定 String[] arr2 = new String[]{"123", "AA", "cc"};//数组类型和长度确定 int[] arr3; Object[] arr4;

    1.3 数组存储的弊端

    ①一旦初始化后,其长度就不可修改

    ②数组中提供的方法非常有限,对于添加、删除、插入数据等操作,非常不便,同时效率不高。

    ③获取数组中实际元素的个数的需求,数组中没有现成的属性或方法可用。

    ④数组存储数据的特点:有序、可重复。对于无序、不可重复的需求,不能满足。

    1.4集合存储的优点

    解决数组存储数据方面的弊端

    2 Collection接口

    2.1 单列集合框架结构

    /----Collection接口:单列集合,用来存储一个一个的对象

    ​ /----list接口:存储有序的、可重复的数据。 —>“动态的数组”

    ​ /----ArrayList、LinkedList、Vector

    ​ /----Set接口:存储无序的、不可重复的数据 —>高中的集合

    ​ /----HashSet、LinkedHashSet、TreeSet

    2.2 Collection接口常用方法

    有15个

    add(Object obj)添加元素

    addAll(Collection coll)将列表coll中的元素全部添加到当前列表

    size()当前列表内的元素个数

    isEmpty()判断当前列表是否为空

    clear()清空当前列表

    remove(Object obj) 删除元素

    removeAll(Collection col)

    removeIf

    contains(Object obj)判断是否包含元素obj,返回boolean

    containsAll(Collection coll)判断当前集合是否包含集合coll,返回boolean

    retainAll(Collection coll),取当前集合和coll的交集,赋给当前集合,有返回值,返回值的类型为boolean

    equals()判断两个集合是否相等

    hasCode(),返回当前集合的哈希值

    toArray(),列表转数组,返回一个Objcet类型的数组

    iterator(),返回一个当前集合的迭代器

    2.3 Collection集合与数组之间的转换

    集合—>数组:toArray()

    //toArray()集合转数组,转换成了一个Object类型的数组 Object[] objects = coll1.toArray(); //foreach循环(增强for循环) for (Object o : objects) { System.out.println(o); }

    数组—>集合:调用Arrays类的静态方法asList(T … t0)

    //数组转集合 List<String> strings = Arrays.asList(new String[]{"AA", "BB", "CC"}); System.out.println(strings); //需要注意的是!基本数据类型的数组,为被认为是一个元素~ //所以如果要解决这个问题,需要使用包装类 List<int[]> ints = Arrays.asList(new int[]{123, 333, 90}); System.out.println(ints); List<Integer> integers = Arrays.asList(new Integer[]{11, 22, 34}); System.out.println(integers);

    2.4 Collection集合存储对象的注意点

    向Collection接口的实现类的对象中添加obj时,要求obj所在类要重写equals()

    2.5 删除Collection元素的三种方式

    Collection collection = new ArrayList(); collection.add("java"); collection.add("bigdata"); collection.add("python"); collection.add("bigdata"); collection.add("bigdata"); //方式一:iterator.remove() //区别于:collection.remove(Object obj) Iterator iterator = collection.iterator(); while (iterator.hasNext()){ if (iterator.next().equals("bigdata")){ iterator.remove(); } } //方式二:contains(Object obj) while(collection.contains("bigdata")){ collection.remove("bigdata") } //方式三:removeAll(Collection coll) Collection coll = new ArrayList(); coll.add("bigdata"); collection.removeAll(coll);

    3 Iterator接口与foreach循环

    3.1 遍历Collection的两种方式

    使用迭代器Iterator

    Iterator iterator = coll.iterator(); //遍历集合 while(iterator.hasNext()){ System.out.println(iterator.next()); }

    使用foreach循环

    //快捷键时iter Object[] objects = coll1.toArray(); for (Object o : objects) { System.out.println(o); }

    3.2 Iterator迭代器

    java.utils包下定义的迭代器接口:IteratorIterator对象称为迭代器(设计模式中的一种),主要用于遍历Collection集合中的元素。

    iterator迭代器的源码分析

    Collection collection = new ArrayList(); collection.add("java"); collection.add("bigdata"); collection.add("python"); collection.add("bigdata"); collection.add("bigdata"); Iterator iterator = collection.iterator(); while (iterator.hasNext()){ if (iterator.next().equals("bigdata")){ iterator.remove(); } } //iterator() /* Iterator 是一个接口,coll调用iterator(),生成一个iterator迭代器是调用ArrayList内的iterator()方法,return new Itr();返回一个Itr对象,Itr实现了Iterator接口;Itr是ArrayList内的一个内部类,它有三个属性,cursor返回值的游标索引值、lastRet辅助游标cursor-1;modCount操作数。 源码: Collection coll = new ArrayList(); public Iterator<E> iterator() { return new Itr(); } private class Itr implements Iterator<E> { int cursor; // 游标,从0开始 int lastRet = -1; // 游标上一个,从-1开始 int expectedModCount = modCount;//操作的次数 */ //hasNext() /* 运行时看ArrayList的hasNext();判断游标索引和有效个数。 源码: public boolean hasNext() { return cursor != size; } */ //next() /* ArrayList的iterator的next方法, 源码: public E next() { checkForComodification();//检查异常 checkForComodifcation()的内部源码 final void checkForComodification() { if (modCount != expectedModCount)//判断集合的操作数和迭代器的操作数是否相等。 throw new ConcurrentModificationException(); } int i = cursor;//定义一个辅助游标,帮助遍历数组;判断的目的 if (i >= size)//size数组的有限数据的个数 throw new NoSuchElementException(); Object[] elementData = ArrayList.this.elementData;//Object[] elementData的地址指向将coll的数组的地址 if (i >= elementData.length)//length数组的实际长度,判断的目的是 throw new ConcurrentModificationException(); cursor = i + 1;//游标先下移,再返回上一个值。 return (E) elementData[lastRet = i];//返回游标的上一个值。 } */ //iterator.remove() /* 源码: public void remove() { if (lastRet < 0) throw new IllegalStateException(); checkForComodification(); try { ArrayList.this.remove(lastRet);//迭代器中的删除还是调用ArrayList中的remove() cursor = lastRet;//删除一个后游标后移到lastRet位置。 lastRet = -1; expectedModCount = modCount;//这是不报错的主要原因! } catch (IndexOutOfBoundsException ex) { throw new ConcurrentModificationException(); } } */ //coll.remove() /* public E remove(int index) { rangeCheck(index); modCount++;//Collection中的删除操作数++,但是在上述checkForComodification(),导致next方法中的迭代器操作数与之不相等,会报异常。 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; } */

    迭代器中remove()的使用

    如果还没调用next()或在上一次调用next方法之后,已经调用了remove方法,再调用remove都会报IllegalStateException迭代器中定义的remove方法不同于Collection集合直接调用remove。 @Test public void test1{ Collection coll = new ArrayList(); coll.add(123); coll.add(456); coll.add(new Person("Jerry",20)); coll.add(new String("Tom")); coll.add(false); //删除集合中"Tom" Iterator iterator = coll.iterator(); while(iterator.hasNext()){ Object obj = iterator.next(); if("Tom".equals(obj)){ //调用iterator的remove() iterator.remove(); } } //遍历集合 iterator = coll.iterator(); while(iterator.hasNext()){ System.out.println(iterator.next()); } }

    3.3 foreach循环(增强for循环)

    jdk 5.0 新特性 – 增强for循环(foreach循环)

    遍历集合举例:

    @Test public void test1(){ Collection coll = new ArrayList(); coll.add(123); coll.add(456); coll.add(new Person("Jerry",20)); coll.add(new String("Tom")); coll.add(false); //for(集合元素的类型 局部变量 : 集合对象) for(Object obj : coll){ System.out.println(obj); } }

    说明

    内部仍然时调用了迭代器

    遍历数组举例:

    @Test public void test2(){ int[] arr = new int[]{1, 2, 3, 4, 5}; //for(数组元素的类型 局部变量:数组对象) for(int i : arr){ System.out.println(i); } }

    4 Collection子接口:List接口

    4.1 存储的数据的特点

    存储有序的、可重复的数据

    4.2 常用方法(记住!!!)

    增:add(Object obj)

    删:remove(Object obj) / remove(int index)

    改:set(int index, Object obj)

    查:get(int index)

    插:add(int index, Object obj)

    长度:size()

    遍历:①Iterator迭代器方式

    ​ ②增强for循环

    ​ ③普通for循环

    增加:add(Object obj) | add(int index, Object obj) | addAll(Collection coll) | addAll(int index, Collection coll)

    注意:add(int index, Object obj),index下标索引值可以=size;但是不能间隔一个去插入

    List list = new ArrayList(); list.add("java"); list.add(123); list.add("123L"); list.add(3, "hadoop");//这可以 list.add(8, "spark");//IndexOutOfBoundsException

    删除:remove

    修改

    4.3 常用实现类

    /----Collection接口:单列集合,用来存储一个一个的对象

    ​ /----List接口:存储有序的、可重复的数据。–>”动态数组“,替换原来的数组

    ​ /----ArrayList:作为List接口的主要实现类;线程不安全,效率高;底层使用Object[] elementData存储

    ​ /----LinkedList:对于频繁的插入、删除操作,使用此类效率比ArrayList高;底层使用双向链表存储

    4.4 源码分析(难点)

    ArrayList的源码分析

    jdk 7 的情况下

    ArrayList list = new ArrayList();//底层创建了长度为10的Object[] 数组elementData;

    list.add(123);//elementData[0] = new Integer(123);

    list.add(11);//如果此次的添加导致底层的elementData数组容量不够,则扩容。

    默认情况下,扩容为原来的容量的1.5倍,同时需要将原有数组中的数据复制到新的数组中。

    结论:建议开发中使用带参数的构造器:

    ArrayList list = new ArrayList(int capacity)

    jdk 8 的情况下

    ArrayList list = new ArrayList(); //底层Object[] elementData初始化为{}。并没有创建长度为10的数组。

    list.add(123);//第一次调用add()时,底层才创建了长度为10的数组,并将数据123添加到elementData[0]

    后续的添加和扩容操作与jdk 7 无异

    结论:jdk 7的ArrayList的对象的创建相当于类似单例的饿汉式,而jdk 8的ArrayList的对象的创建相当于懒汉式,延迟了数组的创建,节省内存。

    //ArrayList的源码分析 @Test public void testArrayList(){ List list = new ArrayList(); //增add(Object obj) list.add("java"); list.add(123); list.add(33); list.add(14423); list.add("123L"); list.add("123L"); list.add("123L"); list.add(5, "hadoop"); list.add("123L"); list.add("123L"); list.add("123L"); System.out.println(list); } /* ArrayList里的属性: this.elementData,是ArrayList的实例化对象的数组。 private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};//jdk1.8以后,默认初始的数组为空。在第一次添加的时候才扩容为10。 1. 初始化,new ArrayList(); public ArrayList() { this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;// } 2. 第一次添加的时候: public boolean add(E e) { ensureCapacityInternal(size + 1);//2.1,minCapacity = size + 1 //1 elementData[size++] = e; return true; } 2.1 private void ensureCapacityInternal(int minCapacity) { //第一次添加的时候elementData==DEFAULT...都是空数组 if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { //最小容量 = max(10, 1) = 10 minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity); } ensureExplicitCapacity(minCapacity);2.2 } 2.2 private void ensureExplicitCapacity(int minCapacity) { modCount++;//操作数++ //如果最小容量 > 数组长度 ---> 10 > 0 if (minCapacity - elementData.length > 0) grow(minCapacity);//2.3 扩容机制 } 2.3 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);//数组扩容 } */

    LinkedList的源码分析

    LinkedList list = new LinkedList();内部声明了Node类型的first和last属性,属性值为null。

    List.add(123);//将123封装到Node中,创建了Node对象。

    其中,Node定义为:体现了LinkedList的双向链表的说话 //LinkedList的源码分析 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; } } //LinkedList的源码分析 @Test public void testLinkedList(){ List list = new LinkedList(); //增add(Object obj) list.add("hadoop"); list.add(1024); list.add("spark"); System.out.println(list); } /* 1. new LinkedList(),实例化LinkedList对象 public LinkedList() {//空参构造器 } 1.1 LinkedList的属性 transient int size = 0; transient Node<E> first;//first和last都是Node类型的节点。LinkedList中只保存了头结点和尾节点的两个属性;还有size节点数量。 transient Node<E> last; 2. add()添加操作。 public boolean add(E e) { linkLast(e);//2.1 添加元素 return true; } 2.1 void linkLast(E e) { final Node<E> l = last;//定义一个辅助节点,保存last节点; final Node<E> newNode = new Node<>(l, e, null);//声明一个newNode节点。 last = newNode;//将链表中的last=newNode if (l == null)//第一次添加的时候l = null,first = newNode first = newNode; else//不是第一次添加的时候,l.next = newNode l.next = newNode; size++;//每添加一个size++ modCount++;//每添加一个操作数++ } */

    Vector的源码分析

    jdk7和jdk8中通过Vector()构造器创建对象时,底层都创建了长度为10的数组。

    在扩容方面,默认扩容为原来的数组长度的2倍。

    //Vector的源码分析 @Test public void testVector(){ List list = new Vector(); list.add("hadoop"); for (int i = 0; i < 9; i++) { list.add("i" + i); } list.add(123); System.out.println(list); } /* 源码: 1. new Vector(),创建Vector对象 public Vector() { this(10);//默认初始容量为10; } 2. add(),添加元素 public synchronized boolean add(E e) { modCount++;//操作数++ ensureCapacityHelper(elementCount + 1);// 2.1判断是否需要扩容 elementData[elementCount++] = e;//添加元素到数组 return true; } 2.1 private void ensureCapacityHelper(int minCapacity) { if (minCapacity - elementData.length > 0) grow(minCapacity);//扩容操作,第一次添加不需要扩容 } 3. 添加到第11个时,需要扩容,扩容为原容量的两倍 private void grow(int minCapacity) { int oldCapacity = elementData.length; int newCapacity = oldCapacity + ((capacityIncrement > 0) ? capacityIncrement : oldCapacity); if (newCapacity - minCapacity < 0) newCapacity = minCapacity; if (newCapacity - MAX_ARRAY_SIZE > 0) newCapacity = hugeCapacity(minCapacity); elementData = Arrays.copyOf(elementData, newCapacity); } 4. 另外需要说明的是,Vector的大部分方法是synchronize的,所以线程安全。 */

    4.5 存储的元素的要求

    添加的对象,所在的类要重写equals()方法

    [面试题]

    面试题:ArrayList、LinkedList、Vector者的异同?同:三个类都是实现了List接口,存储数据的特点相同:存储序的、可重复的数据不同:见上(第3部分+第4部分)

    5 Collection子接口:Set接口

    5.1 存储的数据特点

    无序的、不可重复的元素。

    以HashSet为例说明:

    无序性:不等于随机性。存储的数据在底层数组中并非照数据索引的顺序添加,而是根据数据的哈希值决定的。不可重复性:保证添加的元素照equals()判断时,不能返回true。即相同的元素只能添加一个。

    5.2 元素添加过程(以HashSet为例)

    我们向HashSet中添加元素a,首先会调用元素a所在类的hashCode()方法,计算元素a的哈希值。

    此哈希值接着通过某种算法计算出在HashSet底层数组中的存放位置(即为索引位置)

    判断数组位置上是否已经有元素:

    3.1 如果此位置上没有其他元素,则元素a添加成功。---->情况1

    3.2 如果此位置上有其他元素b(或以链表的形式存在多个元素),则比较元素a与元素b的hash值:

    ​ 3.2.1 如果hash值不相同,则元素a添加成功。---->情况2

    ​ 3.2.2 如果hash值相同,进而需要调用元素a所在类的equals()方法:

    ​ 3.2.2.1 equals()返回true,元素a添加失败

    ​ 3.2.2.2 equals()返回false,元素a添加成功。---->情况3

    对于jdk 7 和jdk 8 添加成功的情况2和情况3而言:

    元素a与已经存在指定索引位置上的数据以链表的方式存储

    jdk7 : 元素a放到数组中,指向原来的元素。

    jdk8: 原来的元素在数组中,指向元素a

    总结:七上八下

    HashSet底层:数组+链表的结构

    5.3 常用方法

    Set接口中没有额外定义新的方法,使用的都是Collection中声明的方法。

    5.4 常用实现类

    /----Collection接口:单列集合,用来存储一个一个的对象

    ​ /----Set接口:存储无序的、不可重复的数据—>高中讲的集合

    ​ /----HashSet:作为Set接口的主要实现类;线程不安全;可以存储null值;

    ​ /----LinkedHashSet:作为HashSet的子类;遍历其内部数据时,可以按照添加的顺序遍历,在添加数据的同时,每个数据还维护了两个引用,记录此数据前一个数据和后一个数据。

    ​ /----TreeSet:可以按照添加对象的指定属性,进行排序。

    5.5 存储对象所在类的要求

    HashSet/LinkedHashSet

    要求:向Set(主要指:HashSet、LinkedHashSet)中添加的数据,其所在的类一定要重写hashCode()和equals()

    要求:重写的hashCode()和equals()尽可能保持一直;相等的对象必须具有相等的散列码

    重写两个方法的小技巧:对象中用作equals()方法比较的Field,都应该用来计算hashCode值。

    TreeSet:

    自然排序中,比较两个对象是否相同的标准为:compareTo()返回0,不再是equals()定制排序中,比较两个对象是否相同的标准为:compare()返回0,不再是equals()

    5.6 TreeSet的使用

    使用说明

    向TreeSet中添加的数据,要求是相同类的对象两种排序方式:自然排序(实现Comparable接口 和 定制排序Comparator)

    常用的排序方式

    方式一:自然排序 @Test public void test1(){ TreeSet set = new TreeSet(); //失败:不能添加不同类的对象 // set.add(123); // set.add(456); // set.add("AA"); // set.add(new User("Tom",12)); //举例一: // set.add(34); // set.add(-34); // set.add(43); // set.add(11); // set.add(8); //举例二: set.add(new User("Tom",12)); set.add(new User("Jerry",32)); set.add(new User("Jim",2)); set.add(new User("Mike",65)); set.add(new User("Jack",33)); set.add(new User("Jack",56)); Iterator iterator = set.iterator(); while(iterator.hasNext()){ System.out.println(iterator.next()); } } 方式二:定制排序 @Test public void test2(){ Comparator com = new Comparator() { //照年龄从小到大排列 @Override public int compare(Object o1, Object o2) { if(o1 instanceof User && o2 instanceof User){ User u1 = (User)o1; User u2 = (User)o2; return Integer.compare(u1.getAge(),u2.getAge()); }else{ throw new RuntimeException("输入的数据类型不匹配"); } } }; TreeSet set = new TreeSet(com); set.add(new User("Tom",12)); set.add(new User("Jerry",32)); set.add(new User("Jim",2)); set.add(new User("Mike",65)); set.add(new User("Mary",33)); set.add(new User("Jack",33)); set.add(new User("Jack",56)); Iterator iterator = set.iterator(); while(iterator.hasNext()){ System.out.println(iterator.next()); } }

    6 Map接口

    Map - 双列集合框架

    6.1 常用实现类结构

    /----Map:双列数据,存储key-value对的数据。—> 类似高中的函数:y = f(x)

    ​ /----HashMap:作为Map的主要实现类:线程不安全的,效率高;可以存储null的key和value

    ​ HashMap底层使用数组+链表(jdk7及以前);数组+链表+红黑树(jdk8);

    ​ /----LinkedHashMap:保证在遍历map元素时,可以按照添加的顺序实现遍历。

    ​ 原因:在原来的HashMap底层结构基础上,添加了一对指针,指向前一个和后一个元素。对于频繁的遍历操作,此类执行效率高于HashMap。

    ​ /----TreeMap:保证按照添加的key-value对进行排序,实现排序遍历。此时考虑key的自然排序或定制排序。

    ​ TreeMap底层使用红黑树结构

    ​ /----Hashtable:作为古老的实现类:线程安全的,效率低,不能存储null的key和value

    ​ /----Properties:常用来处理匹配文件。key和value都是String类型。

    【面试题】

    HashMap的底层实现原理?HashMap和Hashtable的异同?CurrentHashMap与Hashtable的异同?(暂时不讲)

    6.2 存储结构的理解

    Map中的key:无序的、不可重复的,使用Set存储的key

    ​ key所在的类要重写equals()和hashCode() (以HashMap为例)

    Map中的value:无序的、可重复的,使用Collection存储value

    value所在的类要重写equals()

    Map中的entry:无序的、可重复的,使用Set存储entry

    一个键值对:key-value 构成一个Entry对象

    6.3 常用的方法

    添加:put(Object key, Object value)

    ​ putAll(Map map)

    删除:remove(Object key)

    ​ remove(Key k, Value v)

    修改:replace(Key k, newValue)

    ​ replace(Key k, oldValue, newValue)

    查询:get(Object key)

    ​ containsKey(Key k)

    ​ containsValue(Value v)

    遍历:keySet() —> 遍历key值,使用iterator或foreach

    ​ values() —> 遍历value值,使用iterator或foreach

    ​ entrySet() —> 遍历entry

    6.4 内存结构说明(难点)

    HashMap在jdk7中实现原理

    HashMap map = new HashMap():

    在实例化以后,底层创建了长度是**16**的一维数组Entery[] table

    …可能执行过多次put…

    map.put(key1, value1);

    首先调用key1所在类的hashCode()计算key1的哈希值,此时经过某种算法计算后,得到在Entry数组中的存放位置。

    2.1 如果此位置上的数据为空,此时的key1-value1添加成功。 —> 情况1

    2.2 如果此位置上的数据不为空,(意味着此位置上存在一个或多个数据)(以链表形式存在)),比较key1和已经存在的一个或多个数据的哈希值:

    ​ 2.2.1 如果key1的哈希值与已经存在的数据的哈希值不都相同,此时key1-value1添加成功。 —> 情况2

    ​ 2.2.2 如果key1的哈希值和已经存在的某一个数据(key2-value2)的哈希值相同,继续比较:调用key1所在类的equals(key2)方法,比较:

    ​ 2.2.2.1 如果equals()返回false:此时key1-value1添加成功。

    ​ 2.2.2.2 如果equals()返回true:使用value1替换value2

    补充:关于情况2和情况3:此时key1-value1和原来的数据以链表的方式存储。在不断的添加过程中,会涉及到扩容的问题,当超出临界值(且要存放的位置非空)时,扩容。默认的扩容方式:扩容为原来容量16的两倍,将原来的数据复制过来。

    HashMap在jdk8中相较于jdk7在底层实现方面的不同

    new HashMap():底层没有创建一个长度为16的数组jdk 8 底层的数组是:Node[],而非Entry[]首次调用put()方法时,底层创建长度为16的数组jdk7底层结构:数组+链表;jdk8底层结构:数组+链表+红黑树 形成链表时,七上八下(jdk7:新的元素指向旧的元素;jdk8:旧的元素指向新的元素)当数组的某一个索引位置上的元素以链表形式存在的数据个数大于8且当前数组的长度大于64时,此时此索引位置上的所有数据改为红黑树存储。

    HashMap底层典型属性的属性说明

    DEFAULT_INITIAL_CAPACITY:HashMap的默认容量,16

    DEFAULT_LOAD_FACTOR:HashMap的默认加载因子,0.75

    threshold:扩容的临界值=容量×填充因子,16×75 => 12

    TREEIFY_THRESHOLD:Bucket中链表长度大于该默认值,转化为红黑树,8

    MIN_TREEIFY_CAPACITY:桶中的Node被树化时最小的hash表容量:64

    LinkedHashMap的底层实现原理(了解)

    LinkedHashMap底层使用的结构与HashMap相同,因为LinkedHashMap继承于HashMap。

    区别在于:LinkedHashMap内部提供了Entry,替换HashMap中的Node。

    HashMap的源码分析

    Map map = new HashMap(); for (int i = 0; i < 12; i++) { map.put("i-" + i, i); } map.put("j-", 99); //源码分析 /* */

    6.5 TreeMap的使用

    向TreeMap中添加key-value,要求key必须时由同一个类创建的对象因为要照key进行排序:自然排序、定制排序

    6.6 使用Properties读取配置文件

    //Properties:常用来处理配置文件。key和value都是String类型 public static void main(String[] args) { FileInputStream fis = null; try { Properties pros = new Properties(); fis = new FileInputStream("jdbc.properties"); pros.load(fis);//加载流对应的文件 String name = pros.getProperty("name"); String password = pros.getProperty("password"); System.out.println("name = " + name + ", password = " + password); } catch (IOException e) { e.printStackTrace(); } finally { if(fis != null){ try { fis.close(); } catch (IOException e) { e.printStackTrace(); } } }

    7 API总结

    Collection接口

    Processed: 0.011, SQL: 8