1.集合框架

image.png

Map

image.png

1.ArrayList

image.png

  1. 1.ArrayList的底层是一个Object的数组
  2. 第一次初始化是一个长度为0的空数组
  3. transient Object[] elementData 不被序列化
  4. private int size 每次存入一一个元素,size就会+1
  5. 2.默认的初始容量是0
  6. 第一次添加元素后容量为10
  7. private void grow(int minCapacity) {
  8. // overflow-conscious code
  9. int oldCapacity = elementData.length;
  10. int newCapacity = oldCapacity + (oldCapacity >> 1);
  11. if (newCapacity - minCapacity < 0)
  12. newCapacity = minCapacity;
  13. if (newCapacity - MAX_ARRAY_SIZE > 0)
  14. newCapacity = hugeCapacity(minCapacity);
  15. // minCapacity is usually close to size, so this is a win:
  16. elementData = Arrays.copyOf(elementData, newCapacity); --//此处为第一次扩容
  17. }
  18. private static final int DEFAULT_CAPACITY = 10
  19. 3.拥有自动扩容机制,当元素大于当前容量时,会自动扩容,以保证list能够存放添加进去的元素。
  20. 每次扩容都是原来的(1.5)倍
  21. int newCapacity = oldCapacity + (oldCapacity >> 1);
  22. 由于底层是一个数组,因而getset方法都较为简单
  23. ·size() 方法可以获取list的容量
  24. ·set() 方法直接对指定位置进行赋值,且赋值前会进行角标越界检查
  25. ·get() 方法通过角标获得值,由于List是一个Object数组,故而获取后进行转换
  26. ·add() 方法可以添加一个元素

image.png

2.LinkedList

  1. 1.LinkedList是一个双向链表
  2. transient Node<E> first;
  3. transient Node<E> last;
  4. private static class Node<E> {
  5. E item;
  6. Node<E> next;
  7. Node<E> prev;
  8. 节点
  9. Node(Node<E> prev, E element, Node<E> next) {
  10. this.item = element;
  11. this.next = next;
  12. this.prev = prev;
  13. }
  14. }
  15. ····添加
  16. void linkLast(E e) {
  17. final Node<E> l = last;
  18. final Node<E> newNode = new Node<>(l, e, null);
  19. last = newNode;
  20. if (l == null)
  21. first = newNode;
  22. else
  23. l.next = newNode;
  24. size++;
  25. modCount++;
  26. }
  27. ····删除第一个
  28. private E unlinkFirst(Node<E> f) {
  29. // assert f == first && f != null;
  30. final E element = f.item;
  31. final Node<E> next = f.next;
  32. f.item = null;
  33. f.next = null; // help GC
  34. first = next;
  35. if (next == null)
  36. last = null;
  37. else
  38. next.prev = null;
  39. size--;
  40. modCount++;
  41. return element;
  42. }
  43. 2.
  44. 初始为0
  45. transient int size = 0;
  46. transient Node<E> first;
  47. transient Node<E> last;

删除

image.png

3.HashMap

image.png
image.png

  1. 1.HashMap的默认容量为16
  2. 底层维护了一个transient Node<K,V>[] table; node数组
  3. 采用的数组+链表
  4. 每次扩容都是原来的(2)倍
  5. 2.加载因子为0.75,当元素大于等于容量的0.75倍时,就会进行扩容,是为了既要减少元素的碰撞,又充分利用空间
  6. 提高空间利用率和减少查询成本的折中,主要是泊松分布,0.75的话碰撞最小
  7. 3.如果key相同,后者的value就会覆盖掉前者的value,等价于替换
  8. 4.key-value中,key放在一个set集合,value放在一个collection集合
  9. set不可重复,collection可重复
  10. 但是实际上key-value是存放在Node<hash,key,value,Node<K,V> next>中的key,value
  11. setcollection只是创建了一个引用指向了他们两个
  12. 主要是为了方便遍历keyvalue,还会创建一个EntrySet集合,这个集合存放的类型是Entry
  13. 而一个Entry对象就有有kv EntrySet<Entry<key,value>> transient Set<Map.Entry<K,V>> entrySet
  14. Entry就是一个Node节点
  15. 详见HashSet

4.HashSet

  1. 1.HashSet底层是HashMap
  2. public HashSet() {
  3. map = new HashMap<>();
  4. }
  5. 2.元素不重复,可以存放空
  6. 3.数据无序,存放顺序和取出顺序可能不同,因为获得hash值后,在确定元素的索引
  7. 4.扩容
  8. 2倍扩容
  9. 5.为何相同元素添加不进去?
  10. set.add("java")
  11. 1.执行add
  12. private static final Object PRESENT = new Object()
  13. PRESENT占位符
  14. 因为hashSet存放的并不是键值对,所以,mapvalue处都默认添加一个PRESENT占位符
  15. public boolean add(E e) { e "java"
  16. return map.put(e, PRESENT)==null;
  17. }
  18. 2.put方法
  19. public V put(K key, V value) { key = "java" value = PRESENT key是变化的,value不变,因为底层用的hashmap属于键值对,而这里相当于只添加键
  20. 故而值value为固定值
  21. return putVal(hash(key), key, value, false, true);
  22. }
  23. 2.1·hash()方法
  24. 先调用hashcode方法算出hashh,再亦或其hash值并且无符号右移16
  25. 此处为字符串的hashCode方法
  26. public int hashCode() {
  27. int h = hash;
  28. if (h == 0 && value.length > 0) {
  29. char val[] = value;
  30. for (int i = 0; i < value.length; i++) {
  31. h = 31 * h + val[i];
  32. }
  33. hash = h;
  34. }
  35. return h;
  36. }
  37. return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16)
  38. 因此,putVal()方法中的int hash并不是hashCode计算出的值
  39. 这样主要是为了减少数据的碰撞
  40. 3.putVal()方法
  41. final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
  42. boolean evict) {
  43. Node<K,V>[] tab; Node<K,V> p; int n, i; --辅助变量
  44. if ((tab = table) == null || (n = tab.length) == 0)
  45. --如果当前tablenull,或者大小为0,进行第一次扩容
  46. --table就是map的一个属性,是一个数组,用来存放节点
  47. n = (tab = resize()).length;
  48. --resize()后 tabn就变成了16
  49. if ((p = tab[i = (n - 1) & hash]) == null)
  50. 1.--根据传入的key,计算keyhash值,确认这个key该存放到table的那个索引
  51. --并且把这个位置的对象,赋给辅助变量p
  52. 2.--判断这个p是不是空 "java" PRESENT
  53. --如果为空:表示还没有存放数据,就创建一个Node->tab[i] = newNode(hash, key, value, null)真正存放数据的节点
  54. --不为空:
  55. tab[i] = newNode(hash, key, value, null);
  56. else {
  57. Node<K,V> e; K k;
  58. if (p.hash == hash && --1..如果当前索引位置链表的第一个元素的hash==和准备添加的keyhash之一样,到2
  59. --2.若满足以下两个条件之一
  60. --(1)准备加入的keyp指向的Node节点的Key是同一个对象
  61. --(2)不是用一个对象,但是内容相同
  62. --不能加入
  63. ((k = p.key) == key || (key != null && key.equals(k))))
  64. e = p;
  65. --判断p是不是一颗红黑树
  66. else if (p instanceof TreeNode)
  67. e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
  68. else {
  69. --如果索引3是一个链表,有多个值,那么待添加的元素就和他们依次比较内容,如果不同就添加在最后一个的后面
  70. --添加后判断节点数量是否为8,如果为8 使用treeifyBin(tab, hash)把链表转为红黑树
  71. --红黑树化时:如果数组长度<64不会进行树化,而是继续reSize,扩容后,再转化为红黑树
  72. for (int binCount = 0; ; ++binCount) {
  73. if ((e = p.next) == null) {
  74. p.next = newNode(hash, key, value, null);
  75. if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
  76. treeifyBin(tab, hash);
  77. break;
  78. }
  79. --如果值一样,退出循环,比较下一个
  80. if (e.hash == hash &&
  81. ((k = e.key) == key || (key != null && key.equals(k))))
  82. break;
  83. p = e;
  84. }
  85. }
  86. if (e != null) { // existing mapping for key
  87. V oldValue = e.value;
  88. if (!onlyIfAbsent || oldValue == null)
  89. e.value = value;
  90. afterNodeAccess(e);
  91. return oldValue;
  92. --返回空代表成功了
  93. }
  94. }
  95. ++modCount;
  96. if (++size > threshold)
  97. --首次扩容时的临界值threshold=12
  98. --如果++size后大于当前临界值,就进行resize()进行扩容
  99. resize();
  100. afterNodeInsertion(evict);
  101. return null;
  102. }
  103. 4.reSize()方法
  104. final Node<K,V>[] resize() {
  105. 第一次时,table为空
  106. Node<K,V>[] oldTab = table;
  107. int oldCap = (oldTab == null) ? 0 : oldTab.length;
  108. int oldThr = threshold;
  109. --threshold是下一次更变大小的值,或者0
  110. int newCap, newThr = 0; --辅助变量
  111. oldCap首次为0,不进入该方法
  112. if (oldCap > 0) {
  113. if (oldCap >= MAXIMUM_CAPACITY) {
  114. threshold = Integer.MAX_VALUE;
  115. return oldTab;
  116. }
  117. else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
  118. oldCap >= DEFAULT_INITIAL_CAPACITY)
  119. newThr = oldThr << 1; // double threshold
  120. }
  121. else if (oldThr > 0) --需要更变大小的值
  122. newCap = oldThr;
  123. else { --初始值为0
  124. newCap = DEFAULT_INITIAL_CAPACITY;
  125. --首次容量->DEFAULT_INITIAL_CAPACITY,为1<<<4 即初始容量为16
  126. newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY); --他是扩容需要的临界值
  127. --DEFAULT_INITIAL_CAPACITY 加载因子,为0.75
  128. --临界值->newThr = 16*0.75
  129. }
  130. if (newThr == 0) {
  131. float ft = (float)newCap * loadFactor;
  132. newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?
  133. (int)ft : Integer.MAX_VALUE);
  134. }
  135. threshold = newThr;
  136. @SuppressWarnings({"rawtypes","unchecked"})
  137. Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];
  138. --(Node<K,V>[])new Node[newCap] 创建一个长度为16的数组,并转成Node,并且把Node赋给newTab节点
  139. table = newTab;
  140. --把newTab赋值给table,此时table就有16个容量
  141. if (oldTab != null) {
  142. for (int j = 0; j < oldCap; ++j) {
  143. Node<K,V> e;
  144. if ((e = oldTab[j]) != null) {
  145. oldTab[j] = null;
  146. if (e.next == null)
  147. newTab[e.hash & (newCap - 1)] = e;
  148. else if (e instanceof TreeNode)
  149. ((TreeNode<K,V>)e).split(this, newTab, j, oldCap);
  150. else { // preserve order
  151. Node<K,V> loHead = null, loTail = null;
  152. Node<K,V> hiHead = null, hiTail = null;
  153. Node<K,V> next;
  154. do {
  155. next = e.next;
  156. if ((e.hash & oldCap) == 0) {
  157. if (loTail == null)
  158. loHead = e;
  159. else
  160. loTail.next = e;
  161. loTail = e;
  162. }
  163. else {
  164. if (hiTail == null)
  165. hiHead = e;
  166. else
  167. hiTail.next = e;
  168. hiTail = e;
  169. }
  170. } while ((e = next) != null);
  171. if (loTail != null) {
  172. loTail.next = null;
  173. newTab[j] = loHead;
  174. }
  175. if (hiTail != null) {
  176. hiTail.next = null;
  177. newTab[j + oldCap] = hiHead;
  178. }
  179. }
  180. }
  181. }
  182. }
  183. return newTab;
  184. }

首次添加元素Java,计算出hash值后,放在索引为3的位置

image.png

5.LinkedHashSet

链表的物理结构不一定和链表的逻辑结构一样,不同于顺序表

6.数据结构-简单二叉树的实现

  1. package datatest;
  2. /**
  3. * @author 舞遍邓城一条街
  4. */
  5. public class MyTreeTest {
  6. public static void main(String[] args) {
  7. TestTree testTree = new TestTree();
  8. testTree.setRoot(new TestNode(22,"小宝"));
  9. testTree.insert(new TestNode(18,"哈哈"));
  10. testTree.insert(new TestNode(50,"小宝大大"));
  11. testTree.insert(new TestNode(15,"小宝大大"));
  12. testTree.insert(new TestNode(19,"小宝大大"));
  13. testTree.delete(18);
  14. testTree.preList();
  15. System.out.println();
  16. System.out.println(testTree.getNode(50));
  17. }
  18. }
  19. @SuppressWarnings("all")
  20. class TestTree{
  21. private TestNode root;
  22. // 查找节点
  23. public TestNode getNode(int id){
  24. TestNode node = this.root;
  25. while (node.getId()!=id){
  26. if (node.getId()==id) {
  27. return node;
  28. }
  29. if (id>node.getId()) {
  30. node = node.getRight();
  31. } else {
  32. node = node.getLeft();
  33. }
  34. if (node==null) {
  35. break;
  36. }
  37. }
  38. return node;
  39. }
  40. // 插入节点
  41. public void insert(TestNode node){
  42. if (this.root==null){
  43. root = node;
  44. }else {
  45. TestNode cur = this.root;
  46. TestNode parent;
  47. while (true){
  48. parent = cur;
  49. if (node.getId() < parent.getId()){ // 是否去左子树
  50. cur = cur.getLeft();
  51. if (cur == null){
  52. parent.left = node;
  53. return;
  54. }
  55. }else{ // 是否去右子树
  56. cur = cur.getRight();
  57. if (cur == null){
  58. parent.right = node;
  59. return;
  60. }
  61. }
  62. }
  63. }
  64. }
  65. // 查找最值
  66. public TestNode getMin(){
  67. TestNode last = null;
  68. TestNode cur = this.root;
  69. while (cur!=null){
  70. last = cur;
  71. cur = cur.left;
  72. }
  73. return last;
  74. }
  75. public TestNode getMax(){
  76. TestNode last = null;
  77. TestNode cur = this.root;
  78. while (cur!=null){
  79. last = cur;
  80. cur = cur.getRight();
  81. }
  82. return last;
  83. }
  84. // 删除节点
  85. public boolean delete(int id){
  86. TestNode current = root;
  87. TestNode parent = root; // 记录要删除节点的父节点
  88. boolean isLeft = true; // 判断这个节点在父节点的左边还是右边
  89. while (current.getId()!=id){
  90. parent = current;
  91. if (id < current.getId()){
  92. isLeft = true;
  93. current = current.getLeft();
  94. }else{
  95. isLeft = false;
  96. current = current.getRight();
  97. }
  98. if (current == null) {
  99. return false; // 没有找到节点
  100. }
  101. }
  102. // 找到了该节点
  103. if (current.getLeft()==null && current.getRight()==null){
  104. if (current == root){ // 如果是根节点
  105. root = null;
  106. }else if (isLeft){ // 如果是左节点
  107. parent.left = null;
  108. }else { // 如果是右节点
  109. parent.right = null;
  110. }
  111. // 该节点没有右子节点,就让他的左子节点代当前节点
  112. }else if (current.getRight()==null){
  113. if (current == root){
  114. root = current.getLeft();
  115. }else if (isLeft){
  116. parent.left = current.getLeft();
  117. }else {
  118. parent.right = current.getLeft();
  119. }
  120. // 该节点没有左子节点,就让他的右子节点代当前节点
  121. }else if (current.getLeft() == null) {
  122. if (current == root) {
  123. root = current.getRight();
  124. }else if (isLeft){
  125. parent.left = current.getRight();
  126. }else {
  127. parent.right = current.getRight();
  128. }
  129. // 该节点有两个节点,就需要判断再决定让那个节点代替他
  130. } else {
  131. // 先查找该节点的后继节点
  132. TestNode success = getSuccess(current);
  133. if (current == root) {
  134. root = success;
  135. } else if (isLeft) {
  136. parent.left = success;
  137. }else {
  138. parent.right = success;
  139. }
  140. // 让该后继节点的左子树指向原来节点的左子树
  141. success.left = current.getLeft();
  142. }
  143. return true;
  144. }
  145. // 找要删除节点的后继节点
  146. private TestNode getSuccess(TestNode delnode){
  147. TestNode successParent = delnode;
  148. TestNode success = delnode;
  149. // 去右子树查找后继节点
  150. TestNode current = delnode.getRight();
  151. while (current!=null){
  152. // 记录当前节点的父节点
  153. successParent = success;
  154. // 记录当前节点
  155. success = current;
  156. current = current.getLeft();
  157. }
  158. if (success != delnode.getRight()){
  159. successParent.left = success.getRight();
  160. successParent.right = success.getRight();
  161. }
  162. return success;
  163. }
  164. // 遍历树
  165. public void preList(){
  166. if (this.root!=null){
  167. this.root.preList();
  168. }else {
  169. System.out.println("树为空");
  170. }
  171. }
  172. public void midList(){
  173. if (this.root!=null){
  174. this.root.midList();
  175. }else {
  176. System.out.println("树为空");
  177. }
  178. }
  179. public void postList(){
  180. if (this.root!=null){
  181. this.root.postList();
  182. }else {
  183. System.out.println("树为空");
  184. }
  185. }
  186. public TestTree() {
  187. }
  188. public TestNode getRoot() {
  189. return root;
  190. }
  191. public void setRoot(TestNode root) {
  192. this.root = root;
  193. }
  194. }
  195. @SuppressWarnings("all")
  196. class TestNode{
  197. public int id;
  198. public String name;
  199. public TestNode left;
  200. public TestNode right;
  201. // 前序遍历
  202. public void preList(){
  203. System.out.println(this);
  204. if (this.left!=null) {
  205. this.left.preList();
  206. }
  207. if (this.right!=null) {
  208. this.right.preList();
  209. }
  210. }
  211. // 中序遍历
  212. public void midList(){
  213. if (this.left!=null) {
  214. this.left.midList();
  215. }
  216. System.out.println(this);
  217. if (this.right!=null) {
  218. this.right.midList();
  219. }
  220. }
  221. // 后序遍历
  222. public void postList(){
  223. if (this.left!=null) {
  224. this.left.postList();
  225. }
  226. if (this.right!=null) {
  227. this.right.postList();
  228. }
  229. System.out.println(this);
  230. }
  231. @Override
  232. public String toString() {
  233. return "TestNode{" +
  234. "id=" + id +
  235. ", name='" + name + '\'' +
  236. '}';
  237. }
  238. public TestNode() {}
  239. public TestNode(int id, String name) {
  240. this.id = id;
  241. this.name = name;
  242. }
  243. public int getId() {
  244. return id;
  245. }
  246. public void setId(int id) {
  247. this.id = id;
  248. }
  249. public String getName() {
  250. return name;
  251. }
  252. public void setName(String name) {
  253. this.name = name;
  254. }
  255. public TestNode getLeft() {
  256. return left;
  257. }
  258. public TestNode getRight() {
  259. return right;
  260. }
  261. }