image.png
数组是一种简单的线性序列,可以快速地访问数组元素,效率高。如果从效率和类型检查的角度讲,数组是最好的。但不灵活。容量需要事先定义好,不能随着需求的变化而扩容

Collection接口

image.png

  1. //容器常用方法
  2. public class CollectionInterface {
  3. public static void main(String[] args) {
  4. Collection<String> c = new ArrayList<>();
  5. System.out.println(c.size()); //容器元素数量
  6. System.out.println(c.isEmpty()); //判断容器是否为空
  7. c.add("张三"); //添加某个元素
  8. c.add("李四");
  9. System.out.println(c);
  10. System.out.println(c.size());
  11. Object[] objs = c.toArray();
  12. System.out.println(objs); //转换成object数组
  13. c.remove("李四"); //移除某个元素 并不是删除了 存储的只是对象的地址
  14. System.out.println(c);
  15. c.clear(); //清除容器内所有元素
  16. System.out.println(c);
  17. }
  18. }

list

List是有序、可重复的容器,允许多个null元素

  • 有序:List中每个元素都有索引标记。可以根据元素的索引标记(在List中的位置)访问元素,从而精确控制这些元素。
  • 可重复:List允许加入重复的元素。更确切地讲,List通常允许满足 e1.equals(e2) 的元素重复加入容器。

    1. 除了Collection接口中的方法,**List多了一些跟顺序(索引)有关的方法**:

List接口常用的实现类有3个:ArrayList(数组)、LinkedList(链表)和Vector(向量)
image.png

ArrayList

  1. public static void text03() {
  2. List<String> list = new ArrayList<>();
  3. list.add("a");
  4. list.add("b");
  5. list.add("c");
  6. list.add("d");
  7. System.out.println(list);
  8. list.add(2,"e"); //添加指定位置的数据
  9. System.out.println(list);
  10. list.remove(2); //删除指定位置的数据
  11. System.out.println(list);
  12. list.set(2,"e"); //修改指定位置的元素
  13. System.out.println(list);
  14. System.out.println(list.get(2));//返回指定索引位置的数据
  15. list.add("e");
  16. list.add("f");
  17. list.add("g");
  18. System.out.println(list);
  19. System.out.println(list.indexOf("e")); // 返回第一个索引匹配的位置
  20. System.out.println(list.lastIndexOf("e")); //返回最后个索引匹配的位置
  21. }

ArrayList底层是用数组实现的存储特点查询效率高,增删效率低,线程不安全。一般使用它

:List集合中的toString继承自AbstractCollection,它重写了Object里定义的toString方法;所以直接输出集合可以遍历

arraylist重写

  1. //arraylist重写
  2. public class RwArrayList2<E>{ //增加范型
  3. //类的属性全部私有
  4. private Object elementDate[]; //核心数组存储内容
  5. private int size; //数组元素长度
  6. private static final int DEFALT_CAPACITY=10; //静态变量
  7. public RwArrayList2() { //构造器方法
  8. elementDate = new Object[DEFALT_CAPACITY];//默认数组长度
  9. }
  10. public RwArrayList2(int capacity) { //构造器方法
  11. elementDate = new Object[capacity]; //定义数组长度
  12. }
  13. //添加方法
  14. public void add(E obj) {
  15. //扩容操作
  16. if (size == elementDate.length) {
  17. //扩大数组长度的一半
  18. Object[] newarray = new Object[elementDate.length+(elementDate.length>>1)];
  19. //拷贝原数组
  20. System.arraycopy(elementDate, 0, newarray, 0, elementDate.length);
  21. elementDate = newarray; //把老数组指向新数组地址
  22. }
  23. elementDate[size++] = obj;
  24. }
  25. public E get(int index) {
  26. check(index); //检查索引
  27. return (E)elementDate[index];
  28. }
  29. public void set(int index,E s2) {
  30. check(index);
  31. elementDate[index] = s2;
  32. }
  33. public void check(int index) {
  34. //判断索引合法性
  35. if(index < 0 || index > elementDate.length) {
  36. throw new RuntimeException("索引不合法: "+index);
  37. }
  38. }
  39. public void remove(E element) {
  40. //将element与所有元素比较 获得第一个返回true的 返回
  41. for (int i = 0; i < size; i++) {
  42. if(element.equals(get(i))){
  43. remove(i);
  44. }
  45. }
  46. }
  47. //删除操作
  48. public void remove(int index) {
  49. //核心是数组拷贝
  50. int num = elementDate.length-index-1;
  51. if(num >0) {
  52. System.arraycopy(elementDate, index+1, elementDate, index, num);
  53. }
  54. elementDate[size-1] = null;
  55. size--;
  56. }
  57. //重写tostring方法 打印数组内数据
  58. @Override
  59. public String toString() {
  60. StringBuilder b1 = new StringBuilder();
  61. b1.append("[");
  62. for (int i = 0; i < size; i++) {
  63. b1.append(elementDate[i]+" ");
  64. }
  65. b1.setCharAt(b1.length()-1, ']'); //改变最后一个字符
  66. return b1.toString();
  67. }
  68. public static void main(String[] args) {
  69. RwArrayList2 s1 = new RwArrayList2(); //数组对象
  70. for (int i = 0; i < 20; i++) {
  71. s1.add("h"+i);
  72. }
  73. s1.add("a");
  74. s1.add("b");
  75. s1.set(10, "aa");
  76. System.out.println(s1);
  77. System.out.println(s1.get(10));
  78. s1.remove("aa");
  79. System.out.println(s1);
  80. }
  81. }

LinkedList

LinkedList底层用双向链表实现的存储。特点:查询效率低,增删效率高,线程不安全。它的每个数据节点中都有两个指针,分别指向前一个节点和后一个节点
image.png

重写Linkedlist

  1. //重写Linkedlist
  2. public class RwLinkedList <E>{
  3. private Node first;
  4. private Node last;
  5. private int size;
  6. public void add(E element) {
  7. Node node =new Node(element);
  8. if (first == null) {
  9. first = node;
  10. last = node;
  11. }else {
  12. node.previous = last;
  13. node.next = null;
  14. last.next = node;
  15. last = node;
  16. }
  17. size++;
  18. }
  19. public E get(int index) {
  20. if (index<0 || index>size-1) {
  21. throw new RuntimeException("索引不合法:"+index);
  22. }
  23. Node temp = getNode(index);
  24. return temp != null?(E)temp.element:null;
  25. }
  26. //封装获取节点
  27. public Node getNode(int index) {
  28. Node temp = null;
  29. if (index<=(size>>1)) { //size右移一位相当于除以2
  30. temp = first;
  31. for (int i = 0; i < index; i++) {
  32. temp = temp.next;
  33. }
  34. }else {
  35. temp = last;
  36. for (int i = size-1; i > index; i--) {
  37. temp = temp.previous;
  38. }
  39. }
  40. return temp;
  41. }
  42. public void remove(int index) {
  43. if (index<0 || index>size-1) {
  44. throw new RuntimeException("索引不合法:"+index);
  45. }
  46. Node temp = getNode(index);
  47. if(temp!=null) {
  48. Node upNode = temp.previous;
  49. Node dowNode = temp.next;
  50. if(upNode!=null) {
  51. upNode.next = dowNode;
  52. }
  53. if(dowNode != null) {
  54. dowNode.previous = upNode;
  55. }
  56. //删除第一个元素时
  57. if(index == 0) {
  58. first = dowNode;
  59. }
  60. //删除最后一个元素时
  61. if(index == size-1) {
  62. last = upNode;
  63. }
  64. size--;
  65. }
  66. }
  67. public void insert(int index,E element) {
  68. Node newnNode = new Node(element);
  69. Node tempNode = getNode(index);
  70. if (tempNode!=null) {
  71. Node upNode = tempNode.previous;
  72. if (index == 0) { //插入首元素
  73. first = newnNode;
  74. newnNode.next = tempNode;
  75. tempNode.previous = newnNode;
  76. }else if (index == size-1) { //插入尾元素
  77. last = newnNode;
  78. tempNode.next = newnNode;
  79. newnNode.previous = tempNode;
  80. }else {
  81. upNode.next = newnNode;
  82. newnNode.previous = upNode;
  83. newnNode.next = tempNode;
  84. tempNode.previous = newnNode;
  85. }
  86. }
  87. }
  88. //规范输出格式
  89. @Override
  90. public String toString() {
  91. StringBuilder s1 = new StringBuilder();
  92. s1.append("[");
  93. Node temp = first;
  94. while (temp != null) {
  95. s1.append(temp.element+" ");
  96. temp = temp.next;
  97. }
  98. s1.setCharAt(s1.length()-1, ']');
  99. return s1.toString();
  100. }
  101. public static void main(String[] args) {
  102. RwLinkedList<String> list = new RwLinkedList<>();
  103. list.add("a");
  104. list.add("b");
  105. list.add("c");
  106. list.add("e");
  107. list.add("f");
  108. list.add("g");
  109. list.remove(5);
  110. list.insert(1, "aa");
  111. System.out.println(list);
  112. System.out.println(list.get(3));
  113. }
  114. }

Vector

Vector与ArrayList一样,也是通过数组实现的,但它支持线程的同步,即某一时刻只有一个线程能够写Vector.但实现同步需要很高的花费。因此,访问它比访问ArrayList慢

set

Set容器特点:无序、不可重复,只允许一个null元素。无序指Set中的元素没有索引(没有get方法),只能遍历查找;不允许加入重复的元素

  1. //基本用法
  2. public class TestHashSet {
  3. public static void main(String[] args) {
  4. Set<String> s1 = new HashSet<String>();
  5. s1.add("a");
  6. s1.add("b");
  7. s1.add("a");
  8. System.out.println(s1);
  9. s1.remove("a");
  10. System.out.println(s1);
  11. Set<String> s2 = new HashSet<String>();
  12. s2.add("A");
  13. s2.addAll(s1);
  14. System.out.println(s2);
  15. }
  16. }

TreeSet

TreeSet底层实际是用TreeMap实现的,通过key来存储Set的元素。TreeSet内部需要对存储的元素进行排序,因此,我们对应的类需要实现Comparable接口。

  1. public class TreeSet {
  2. public static void main(String[] args) {
  3. Set<Integer> s1 = new java.util.TreeSet<Integer>();
  4. s1.add(3);
  5. s1.add(2);
  6. s1.add(1);
  7. //按元素递增模式输出
  8. for(Integer i:s1) {
  9. System.out.println(i);
  10. Set<employee2> s2 = new java.util.TreeSet<>();
  11. s2.add(new employee2(1001, "A", 10000));
  12. s2.add(new employee2(1003, "B", 20000));
  13. s2.add(new employee2(1004, "C", 30000));
  14. for(employee2 i:s2) {
  15. System.out.println(i);
  16. }
  17. }
  18. }

Map接口

Map就是用来存储“键(key)-值(value) 对”的。 Map类中存储的 “键值对” 通过键来标识,所以“键对象”不能重复。这就是一种成对存储的关系。
image.png

Map 接口的实现类有HashMap、TreeMap、HashTable、Properties等

常用方法

  1. //常用方法
  2. public static void main(String[] args) {
  3. Map<Integer, String> m1 = new HashMap<>();
  4. m1.put(1, "a");
  5. m1.put(2, "b");
  6. m1.put(3, "c");
  7. System.out.println(m1.get(1));
  8. System.out.println(m1.size());
  9. System.out.println(m1.containsValue("a"));
  10. Map<Integer, String> m2 = new HashMap<>();
  11. m2.put(4, "A"); //注意键不能重复
  12. m2.put(5, "B");
  13. m2.put(6, "C");
  14. m1.putAll(m2);
  15. System.out.println(m1);
  16. }

HashMap采用哈希算法实现,是Map接口最常用的实现类。 由于底层采用了哈希表存储数据,要求键不能重复,如果发生重复,新的键值对会替换旧的键值对。 HashMap在查找、删除、修改方面都有非常高的效率。

HashTable类和HashMap用法几乎一样,底层实现几乎一样,只不过HashTable的方法添加了synchronized关键字确保线程同步检查,效率较低

HashMap与HashTable的区别

  • HashMap
    • 线程不安全,效率高
    • 允许key或value为null
    • 初始容量是16,扩容是2n
    • 在首次调用put方法时放入元素时才会初始化数组
  • HashTable
    • 线程安全,效率低
    • 不允许key或value为null
    • 初始容量是11,扩容2n+1
    • 构造函数里面就初始化数组


TreeMap

TreeMap和HashMap实现了同样的接口Map,因此,用法对于调用者来说没有区别。HashMap效率高于TreeMap;在需要排序的Map时才选用TreeMap。

  1. public class TestTreeMap {
  2. public static void main(String[] args) {
  3. Map<Integer, String> map1 = new TreeMap<>();
  4. map1.put(10, "a");
  5. map1.put(20, "b");
  6. map1.put(30, "c");
  7. //按照key递增的方式排序
  8. for(Integer key:map1.keySet()) {
  9. System.out.println(key+"--"+map1.get(key));
  10. }
  11. Map<employee2, String> treeMap = new TreeMap<>();
  12. treeMap.put(new employee2(1001, "张三",6000),"。。。");
  13. treeMap.put(new employee2(1002, "张",3000),"。。");
  14. treeMap.put(new employee2(1003, "张4",5000),"。");
  15. for(employee2 key:treeMap.keySet()) {
  16. System.out.println(key+"--"+treeMap.get(key));
  17. }
  18. }
  19. }
  20. //Comparable接口可按照自定义变量进行递增
  21. class employee2 implements Comparable<employee2>{
  22. int id;
  23. String name;
  24. int salary;
  25. public employee2(int id, String name, int salary) {
  26. super();
  27. this.id = id;
  28. this.name = name;
  29. this.salary = salary;
  30. }
  31. @Override
  32. public String toString() {
  33. return "id:"+id+" name:"+name+" salary:"+salary;
  34. }
  35. //按照工资排序
  36. @Override
  37. public int compareTo(employee2 o) {//负数:小于 0:等于 正数:大于
  38. if(this.salary > o.salary) {
  39. return 1;
  40. }else if (this.salary < o.salary) {
  41. return -1;
  42. }else { //工资相等
  43. if(this.id > o.id) {
  44. return 1;
  45. }else if (this.id <o.id) {
  46. return -1;
  47. }else {
  48. return 0;
  49. }
  50. }
  51. }
  52. }

接口使用的选择

1.png

Iterator迭代器遍历容器

遍历容器元素(List/Set/Map)
hashnext():如果任有元素可以迭代,则返回true

  1. public class TestIterator {
  2. public static void main(String[] args) {
  3. TestIterator();
  4. TestIterator2();
  5. TestIterator3();
  6. TestIterator4();
  7. }
  8. public static void TestIterator() {
  9. List<String> list = new ArrayList<>();
  10. list.add("a");
  11. list.add("b");
  12. list.add("c");
  13. //用迭代器遍历list
  14. for(Iterator<String> iterator= list.iterator(); iterator.hasNext();) {
  15. String temp = iterator.next();
  16. System.out.println(temp);
  17. }
  18. System.out.println("******");
  19. }
  20. public static void TestIterator2() {
  21. Set<String> set1 = new HashSet<>();
  22. set1.add("a");
  23. set1.add("b");
  24. set1.add("c");
  25. //用迭代器遍历set
  26. for(Iterator<String> iterator= set1.iterator(); iterator.hasNext();) {
  27. String temp = iterator.next();
  28. System.out.println(temp);
  29. }
  30. System.out.println("******");
  31. }
  32. public static void TestIterator3() {
  33. Map<Integer, String> map1 = new HashMap<>();
  34. map1.put(1, "a");
  35. map1.put(2, "b");
  36. map1.put(3, "c");
  37. //获取hashmap的位桶数组
  38. Set<Entry<Integer, String>> se = map1.entrySet();
  39. //用迭代器遍历map
  40. //法一:取出map内的每一个结点,再遍历每一个节点的key
  41. for(Iterator<Entry<Integer, String>> iterator= se.iterator(); iterator.hasNext();) {
  42. Entry<Integer, String> temp = iterator.next();
  43. System.out.println(temp.getKey()+"--"+temp.getValue());
  44. }
  45. System.out.println("******");
  46. }
  47. public static void TestIterator4() {
  48. Map<Integer, String> map1 = new HashMap<>();
  49. map1.put(1, "a");
  50. map1.put(2, "b");
  51. map1.put(3, "c");
  52. //通过map1获取key和value的集合 再去遍历它们
  53. Set<Integer > keySet = map1.keySet();
  54. //用迭代器遍历map 法2:直接遍历map节点的集合
  55. for(Iterator<Integer> iter = keySet.iterator(); iter.hasNext();) {
  56. Integer key = iter.next();
  57. System.out.println(key+"--"+map1.get(key));
  58. }
  59. System.out.println("******");
  60. //jdk8新特性 函数式编程
  61. map.forEach((key,value)-> System.out.println(key+"="+value));
  62. }
  63. }

Collections工具类

类 java.util.Collections 提供了对Set、List、Map进行排序、填充、查找元素的辅助方法。

  1. public class TestCollections {
  2. public static void main(String[] args) {
  3. List<String> list = new ArrayList<String>();
  4. for(int i=0; i<10; i++) {
  5. list.add("a"+i);
  6. }
  7. System.out.println(list);
  8. //逆序排序
  9. Collections.reverse(list);
  10. System.out.println(list);
  11. //随机排列数组中的元素
  12. Collections.shuffle(list);
  13. System.out.println(list);
  14. //正常排序
  15. Collections.sort(list);
  16. System.out.println(list);
  17. //二分查找
  18. System.out.println(Collections.binarySearch(list, "a1"));
  19. //用一个特定的对象覆盖重写整个容器
  20. Collections.fill(list, "b");
  21. System.out.println(list);
  22. }
  23. }