数据结构- 集合(List、Set、Map)- List接口- Set接口- ArrayList和LinkedList的使用场景- Map接口- 哈希碰撞(哈希冲突)- 集合小节

目录

数据结构

集合

List接口

ArrayList和LinkedList的使用场景

Set接口

Map接口

哈希碰撞(哈希冲突)

集合小节


数据结构

概念:保存数据的一种方式

常见的数据结构

  1. 通过数组来保存,基于数组的数据结构(动态数组,长度可变的数组)

    基于数组的结构的优缺点

    优点:查改快

    1)、通过下标查询元素,效率高

    2)、通过下标修改元素,效率高

    缺点:增删插慢

    1)、在需要扩容的时候:添加慢,删除慢,插入元素慢

  2. 通过一个对象变量来保存数据,基于变量保存数据的结构称为链表结构

    优点:增删插快

    缺点:查改慢

  3. 队列

    单向队列:一个是进口,一个是出口,先进先出(FIFO)

    双向队列:两端既是进口又是出口

  4. 栈结构:只有一个口,既是进口也是出口

    先进后出:FILO(后进先出:LIFO)

  5. 树形结构:是链表的一种变形结构

集合

概念:基于某种数据结构的容器

集合的体系结构:

Collection接口:所有集合框架的根接口(父接口):定义方法的标准

常用子接口:List Set Queue等

1. 第一个子接口:list 可以存放重复元素,而且有序(有序:存入的顺序和取出来的顺序一致(存取一致)),有序可重复

使用接口的实现类(我们不想要重写接口中的每个方法,所以就用一个抽象类来实现接口,先重写一次,充当过滤器的作用,这样后续就可以选择想要的方法了)

abstract class AbstractList implements List {
   // 实现list里面的所有的抽象方法
}
class ArrayList extends AbstractList implements List {
   // 继承AbstractList后,想重写哪个就哪个 再实现list接口是为了看上去规范,因为接口的方法已经被AbstractList重写了
}

List接口的实现子类:

ArrayList:基于数组的集合(线程不安全)

LinkedList:基于链表的集合

Vector:基于数组的集合(线程安全)

2. 第二个子接口:set 不能存放相同的元素(去重),无序(存取不一致),无序不可重复

3. 还有其他的子接口,这里是常用的两个

List接口

  • List接口的实现类

一:ArrayList: 基于数组的集合 public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, Serializable

  • 构造方法:

    1. ArrayList( ) 构造一个初始容量为十的空列表。

    ArrayList list = new ArrayList(); //  创建ArrayList的对象(基于数组的集合)  但是这时候jvm还没有将容量扩容到十,只有使用这个对象的时候才会扩容,这是jvm的一种延迟机制,防止对象没有使用从而导致内存的浪费
    1. ArrayList(int initialCapacity) 构造具有指定初始容量的空列表。

    ArrayList list = new ArrayList(20); //  创建ArrayList的对象  但是初始化的长度是20

  • 常用方法:

    1. (1)add(E e) 将指定的元素追加到此列表的末尾。 (2)add(int index, E element) 在此列表中的指定位置插入指定的元素。

    ArrayList list = new ArrayList(); //  创建ArrayList的对象(基于数组的集合)
    list.add(true);  // (1)
    System.out.println(list); // 打印[true]
    ​
    list.add(0,'a'); // (2)
    System.out.println(list); // 打印[a, true]
    ​
    ArrayList list2 = new ArrayList(); // 创建ArrayList的新对象
    list2.add(list); // (1) 将对象list添加到了list2中  也就是将集合作为一个值添加到集合
    list2.add(1); // 自动将int类型的1自动装箱成integer类型
    System.out.println(list2); // 打印[[a, true], 1]
    1. size( ) 返回此列表中的元素数

    ArrayList list = new ArrayList(); //  创建ArrayList的对象(基于数组的集合)
    list.add(true);
    list2.add(1);
    System.out.println(list.size()); // 打印2
    1. addAll(int index, Collection<? extends E> c) 将指定集合中的所有元素插入到此列表中,从指定的位置开始。

    ArrayList list = new ArrayList(); //  创建ArrayList的对象(基于数组的集合)
    list.add(true);  // (1)
    list.add("a");  
    list2.add(1);
    ​
    ArrayList list2 = new ArrayList(); // 创建ArrayList的新对象
    list2.addAll(0,list); // 将list集合里面元素一个一个添加list2里面
    System.out.println(list2); // 打印[true, a, 1]
    1. contains(Object o) 判断集合中是否存在该对象,有则返回 true

    ArrayList list = new ArrayList(); //  创建ArrayList的对象(基于数组的集合)
    list.add(true);  // (1)
    System.out.println(list.contains(true)); // 打印 true
    1. get(int index): 通过指定下标获取对象

    ArrayList list = new ArrayList(); //  创建ArrayList的对象(基于数组的集合)
    list.add("a");  
    list.add(true);  // (1)
    ​
    System.out.println(list.get(0)); // 打印 a
    1. indexOf(Object o) : 查询对象在集合中第一次出现位置,找不到则返回-1

    ArrayList list = new ArrayList(); //  创建ArrayList的对象(基于数组的集合)
    list.add(true);  // (1)
    list.add("a");  
    System.out.println(list.indexOf('a')); // 打印 1 
    1. lastIndexOf(Object o) 返回此列表中指定元素的最后一次出现的索引,如果此列表不包含元素,则返回-1。

    ArrayList list = new ArrayList(); //  创建ArrayList的对象(基于数组的集合)
    list.add(true);  // (1)
    list.add("a");  
    list.add("a");  
    System.out.println(list.lastIndexOf('a')); // 打印2
    1. (1) remove(int index) :通过下标删除指定对象 (2) remove(Object o) :删除指定对象 (3) removeAll(Collection<?> c) 从此列表中删除指定集合中包含的所有元素。

    ArrayList list = new ArrayList(); //  创建ArrayList的对象(基于数组的集合)
    list.add(true);  // (1)
    list.add("a");  
    list.add("a");  
    ​
    list.remove(0); // 删除下标为0的对象
    System.out.println(list); // 打印[a, a]
    ​
    list.remove("a");  // 直接删除a这个对象
    System.out.println(list);  // 打印[a]
    ​
    list.removeAll(list); // 删除这个列表中的所有元素
    System.out.println(list); // 打印[ ]
    1. set(int index, E element) 用指定的元素替换此列表中指定位置的元素。

    ArrayList list = new ArrayList(); //  创建ArrayList的对象(基于数组的集合)
    list.add(true);  // (1)
    list.add("a"); 
    ​
    list.set(0,"张飞");  // 将下标为0的那个元素,换成"张飞"
    System.out.println(list); // 打印[张飞, a]

  • ArrayList集合的遍历:

    // 方法1: 循环(for)
    ArrayList list = new ArrayList(); //  创建ArrayList的对象(基于数组的集合)
    for (int i = 0; i < list.size(); i++) {
        System.out.println(list.get(i));
    }
    ​
    // 方法2:foreach循环
    for (Object o : list) {
        System.out.println(o);
    }
    ​
    // 方法3:迭代器   (1)单向迭代器  (2)双向迭代器
    // 单向迭代器:只能从第一个元素到最后一个元素  iterator() 
    Iterator it = list.iterator();// 获取遍历list集合的迭代器
    while (it.hasNext()) { // 判断后面一个元素是否有值
        System.out.println(it.next());  // 有就输出
    }
    ​
    // 双向迭代器:可以从后往前迭代     listIterator(int index)  
    ListIterator li = list.listIterator(); // 获取遍历list集合的迭代器   不管是单向迭代器还是双向迭代器指针默认从第一个元素开始
    while (li.hasNext()) {  // 正向迭代
        System.out.println(li.next());
    }
    ​
    System.out.println("===========================");
    ​
    while (li.hasPrevious()) {  // 反向迭代   注意:这里由于上面的正向迭代已经执行到了最后,此时指针已经到了最后,所以hasPrevious()里面不用加参数就本身就是向前迭代,但是如果前面没有那个正向迭代,上面创建迭代器的时候就必须要给参数,写需要从哪个位置开始的 如:listIterator(list.size())  
        System.out.println(li.previous()); 
    }
    ​

二:LinkedList: 基于链表的集合 public class LinkedList<E>extends AbstractSequentialList<E>implements List<E>, Deque<E>, Cloneable, Serializable

  • 构造方法:

  1. LinkedList( ) 构造一个空列表

LinkedList list = new LinkedList(); // 创建LinkedList对象
  1. LinkedList(Collection<? extends E> c) 构造一个包含指定集合的元素的列表,按照它们由集合的迭代器返回的顺序

LinkedList linkedList = new LinkedList(); // 创建LinkedList对象
LinkedList linkedList2 = new LinkedList(linkedList); // 将linkedList的所以元素放进linkedList2

  • 常用方法:大多跟ArrayList的方法一样 以下是实现其他接口的方法

LinkedList实现的队列(双向队列) implements Deque

LinkedList栈结构 栈的特征:FILO

1. (1) add(E e):在此双端队列的尾部 (2) addFirst(E e) 插入此双端队列的前面 (3) addLast(E e) 双端队列的末尾指定元素

2. getFirst( ):检索,但不删除,这个deque的第一个元素 getLast( ):检索,但不删除,这个deque的最后一个元素。

LinkedList list = new LinkedList(); // 创建LinkedList对象
// 在头尾操作的方法  
list.addFirst("关羽");  // 最前面
list.addLast("刘备");   // 最后面
list.add("张飞");  // 最后面
System.out.println(linkedList); // 打印 [张飞, 关羽, 刘备]
System.out.println(linkedList.getLast()); // 刘备  可以获取最后一位的元素
System.out.println(linkedList.getFirst()); // 张飞  可以获取第一位的元素

3. removeFirst( ):检索并删除此deque的第一个元素。 removeLast( ):检索并删除此deque的最后一个元素。

list.removeFirst(); // 删除第一个元素
list.removeLast(); // 删除最后一个元素
System.out.println(list);  // 打印[关羽]

4. element( ):检索但不删除该deque的第一个元素

LinkedList list = new LinkedList(); // 创建LinkedList对象
list.addFirst("关羽");  // 最前面
list.addLast("刘备");   // 最后面
System.out.println(list.element()); // 打印关羽

5. offer(E e):将指定的元素插入在该deque的尾部 offerFirst(E e):在此deque的前面插入指定的元素 offerLast(E e) :在此deque的末尾插入指定的元素

LinkedList list = new LinkedList(); // 创建LinkedList对象
list.offer("关羽");  // 最前面
list.offerFirst("刘备");   // 最后面
list.offerLast("张飞");  // 最后面
System.out.println(linkedList); // 打印 [张飞, 关羽, 刘备]

6. peek( ):检索但不删除此deque的第一个元素 跟element( )方法类似

7. poll( ):检索并删除此deque的第一个元素 pollFirst( ):检索并删除此deque的第一个元素 pollLast( ):检索并删除此deque的最后一个元素

LinkedList list = new LinkedList(); // 创建LinkedList对象
list.offer("关羽");  // 最前面
list.offerFirst("刘备");   // 最后面
list.offerLast("张飞");  // 最后面
list.poll();
list.pollFirst();
list.pollLast();
System.out.println(list); // 打印[]

8. push(e):压栈,将对象放入栈内存 pop():出栈,将栈里面对象取出

LinkedList list = new LinkedList();
// push(e):压栈,将对象放入栈内存
list.push("a");
list.push("b");
list.push("c");
System.out.println(list); // 打印[c, b, a]
// pop():出栈,将栈里面对象取出    先进后出
System.out.println(list.pop()); // 将 c 先出栈  所以打印c
System.out.println(list); // 打印[b, a] 因为c已经出栈

  • LinkedList集合的遍历:

LinkedList list = new LinkedList(); // 创建LinkedList对象
Iterator it = list.iterator(); // 获取遍历list集合的迭代器
​
while (it.hasNext()) {  // 判断后面一个元素是否有值
    System.out.println(it.next());
}
​
System.out.println("======================"); // 分隔一下
ListIterator li = list.listIterator(list.size()); // 反向迭代 注意:这里必须要加上list.size()或者写一个int类型的参数,否则不会打印值,因为现在是基于链表的集合,默认就是从开头开始遍历的
while (li.hasPrevious()) {
    System.out.println(li.previous());
}
​

ArrayList和LinkedList的使用场景

  1. 底层结构不同

  2. ArrayList底层是数组:查改性能好,增删插性能差

  3. LinkedList底层是一个双向链表,增删插性能好,查改性能差 ,底层还是一个队列,还是一个栈

Set接口

Set集合:无序不重复 无序:存取不一致 不重复:元素放入集合之前或做判断

  • set接口的实现类

一、HashSet

构造方法:

  1. HashSet() 构造一个新的空集合; 背景HashMap实例具有默认初始容量(16)和负载因子(0.75)。

  2. HashSet(Collection<? extends E> c) 构造一个包含指定集合中的元素的新集合。

特点:

  1. 底层使用哈希表来实现(底层是一个数组,数组里面保存一个单向链表)的集合

  2. 不允许元素重复,元素是无序的

去重机制:

  1. 将要添加到HashSet集合元素的hashCode值拿到

  2. 在集合去查找是否存在这个hashCode值

    不存在,说明这个元素在集合中不存在,可以执行添加

    存在,就来让这个元素.equals(hashCode值相同元素),

    不相等false,说明这个元素在集合中不存在,执行添加,相等true,说明元素在集合中已经存在了,那就不添加

作用:HashSet的作用就是用来去重

public class SetDemo1 {
    public static void main(String[] args) {
        // 创建一个HashSet集合
        HashSet set = new HashSet(); // 哈希表(散列表)
        // 常用方法
        set.add("abc");
        set.add(true);
        set.add(123);
        set.add("波多野");
        set.add("波多野");
        set.add("波多野");
        System.out.println(set); // 打印[波多野, abc, 123, true]  无序的
        // 遍历
//        for (Object o : set) {
//            System.out.println(o);
//        }
        Iterator it = set.iterator();  // 创建一个单向构造器
        while (it.hasNext()) {
            System.out.println(it.next()); // 遍历
        }
    }
}

  • LinkedHashSet 底层哈希表+单向链表(记录顺序) 等于是有序的HashSet

二、TreeSet

构造方法:

  1. TreeSet() 构造一个新的,空的树组,根据其元素的自然排序进行排序。

  2. TreeSet(Comparator<? super E> comparator) 构造一个新的,空的树集,根据指定的比较器进行排序。

特点:

  1. 无序不可重复

  2. 底层使用树结构来实现的集合

  3. 树型结构是提高查询效率,将树上的元素按大小排序

  4. 在使用TreeSet,该集合中只能放同一种类型的元素,第一个添加的元素类型,就是这个集合中能放的类型

去重机制:

  1. 通过两个接口来完成,添加元素到集合,这个元素会和集合里面所有的元素依次调用compareTo()进行比较,返回0,就说明该元素在集合已经存在了,就不添加了

  2. 元素会根据类里面写compareTo()的代码来进行排序

  3. 作用就是对元素进行排序,注意放入TreeSet的元素必须是同一类型,必须要实现Comparable接口

> 比较对象大小有两种方式
   1.定制排序 需要实现Comparator接口
   2.自然排序 需要实现Comparable接口
         // 以下是第一种
        //  需要单独写一个MyCompartor()类 
        Cat c1 = new Cat("咪咪", "公", 2);
        Cat c2 = new Cat("哆哆", "母", 5);
        Cat c3 = new Cat("啦啦", "公", 1);
// 比较c1、c2和c3的大小,比较对象的大小
        TreeSet set = new TreeSet(new MyCompartor()); // Comparator:定制排序 
        set.add(c1);
        set.add(c2);
        set.add(c3);
        System.out.println(set);
​
        // 以下是第二种
       // 使用Comparable:比较对象大小的接口
        p1.compareTo(p2) //  1、返回正数说明p1对象大于p2对象  2、返回负数说明p1小于p2   3、返回0说明p1等于p2
​
       // 以下是他需要重写的部分
    @Override
    public int compareTo(Object o) {
        // 对象进行大小比较其实就是对象的属性值进行比较
        Pig p = (Pig)o;
        if(!this.name.equals(p.name)) { // 两个对象的名字不同,就比较name
            return p.name.hashCode() - this.name.hashCode(); // 三个之一  0 整数 负数
        }else if(this.age != p.age) { // 上面的name相同  这里的age不同,就比较age
            return p.age - this.age; // 这里如果是p对象在this前面就是逆序 反之就是正序
        }
        return 0; // 属性值都相等返回0
    }
}

Map接口

概念:保存一对值

Map:映射(键值对,由一个键指向一个值)

例:成都 四川 :这是两个值,成都称为键(key),四川称为是值(value),key value:称为一对值(键值对)

注意:

  1. Map中键不能重复,值可以重复,一个键只能指向一个值

  2. Map是映射的顶级父类,是一个接口

Map的实现类

一、HashMap:就是一个哈希表(散列表)

构造方法:

  1. HashMap()构造一个空的HashMap ,默认初始容量(16)和默认负载系数(0.75)。

  2. HashMap(Map<? extends K,? extends V> m) 构造一个新的 HashMap与指定的相同的映射 Map

底层:数组,数组中保存了一个单向链表,链表在满足条件下回转为树结构(红黑树)

在数组保存了哪些信息? 键的hashCode、键、值、下一个对象

 // 创建HashMap集合对象
        HashMap map = new HashMap(); // 创建长度为16的数组,数组的每一个元素有一个空链表,map保存的是一对值
// put(K key, V value) : 向map集合添加一对值
        map.put("3838438", "航空飞机");
​
// 首次存放值的过程
key:"3838438"
1、获取键的hashCode,19(举例)
2、计算这对值放入数组的下标位置:用键的hash值 % 当前数组的长度,19 % 16,计算的结果是3
那么这对值应该放入下标为3的位置
3、判断下标为3的位置上有没有值:
     没有:直接将这对值放入这个位置
     有:将这个值接在这个位置上存在值的后面
    
        map.put("3838438", "老王");
// 老王会覆盖掉航空飞机
map中键不允许重复
去重的机制:键的hashCode + equals
1、key:"3838438",将键的哈希值获得
2、去这个map集合中查看有没有相同哈希值的键
     没有:说明键不重复  计算放入的位置用键的hash值 % 当前数组的长度      
     有:将这个键.equals(哈希值相同的键),结果是true,说明键已经存在,然后覆盖值,结果false
​

常用方法:

  1. put(K key, V value) 将指定的值与此映射中的指定键相关联。

HashMap hm = new HashMap(); // 创建一个HashMap的对象
hm.put(1,"张飞"); 
hm.put(2,"赵云");
System.out.println(hm);// 打印{1=张飞, 2=赵云}
  1. get(Object key) 返回到指定键所映射的值,或 null如果此映射包含该键的映射。

System.out.println(hm.get(1)); // 根据传入键获取值
// 打印张飞
  1. entrySet() 返回此地图中包含的映射的Set视图。

// 遍历map集合,Entry:键值对
// 遍历map的第一种方式:同时获取键和值,entrySet()
// 1
Set set = hm.entrySet();// 获取所有的键值对
System.out.println(set); // 打印[1=张飞, 2=赵云]
while (it.hasNext()) {
    Object next = it.next();
    Map.Entry entry = (Map.Entry) next;
    Object key = entry.getKey(); // 获取键
    Object value = entry.getValue(); // 获取值
    System.out.println(key + ":" + value);
}
// 遍历map的第二种方式:只获取键keySet()
// 2
Set set = hm.keySet();
for (Object key : set) {
    System.out.println(key + ":" + hm.get(key));
}
// 遍历map第三种方法:只获取值,values()
// 3
 Collection values = hm.values();
 Iterator it = values.iterator();
 while (it.hasNext()) {
     Object next = it.next();
     System.out.println(next);
 }
  • 子类LinkedHashMap类:链表 + 哈希表

    概念:保证键是有序的

二、Hashtable类 implements Map

概念:

1)、底层和HashMap相似

2)、不允许null为键null为值(空值空键)

Hashtable tab = new Hashtable();
tab.put(null,null); // 报错NullPointerException
// HashMap能装

3)、线程安全的

  • 子类Properties类(HashTable的子类)

    概念:Properties解析(读取)属性文件

    作用:为什么要将Entry放到属性文件中? 为了解决硬编码问题,通过修改配置文件可以方便的修改代码中的参数,实现不用改class文件即可灵活变更参数,减少代码的维护成本和提高开发效率。

    方法:getProperty(String key) 使用此属性列表中指定的键搜索属性

    Properties pp = new Properties();
    // load():将属性文件中键值对(Entry)加载到Properties对象里面
    pp.load(new FileInputStream("e:/name.properties"));
    // 取出pp中键值对
    System.out.println(pp); //打印你在name中写的键值对
    String s = pp.getProperty("110"); // 找到键为110的值
    System.out.println(s);

概念:

1)、底层也是一个map集合(保存键值对 Entry)

2)、 用来读取属性文件(什么是属性文件:保存键值对的文件)

3)、以.properties结尾的文件就是属性文件

三、TreeMap类 顶级的父接口都也是Map

概念:底层是一个树型结构,对元素进行排序(针对键来排序),是无序的map集合
TreeMap map = new TreeMap(); // 树型结构的map集合
map.put(1,"abc"); // 键只是同一种类型
map.put(23,"bbc");
map.put(2,"cbc");
System.out.println(map);

小结:Map集合的体系结构

-Map:映射根接口

     -HashMap:底层哈希表

          -LinkedHashMap:;链表 + 哈希表

     -HashTable:线程安全,且不允许空值空键

         -Properties:解析属性文件

     -TreeMap:底层是树型结构,对键进行排序

哈希碰撞(哈希冲突)

概念:在计算存储数据的位置的时候,这个位置上已经有值,这就是哈希碰撞

HashSet的值就是存在HashMap的键上 (HashSet只有一个值,且不重复就是因为如此)

链表转为红黑树的过程 当一个链表上的元素个数(链表的节点数)大于等于8个,看这个集合中元素个是否大于等于64 没有:底层数组做一次扩容:当前长度的2倍 有:这个链表就会形成红黑树 (提高查询效率)

当我们做删除的时候 原来是红黑树的,如果元素个数小于等于6个,那么这个红黑树又会转为链表

集合小节

1、体系结构 Collection

  • List:有序可重复 实现类: -ArrayList:底层是一个数组,查改快,增删插慢 -LinkedList:底层是一个双向链表,双向队列,栈 (1)增删插快,查改慢 (2) 对首尾操作快 (3)栈:push(),pop() -Vector:线程安全的动态数组

  • Set:无序不可重复 -HashSet:底层是一个哈希表,去重的原理:hashCode + equals,作用:用来去重的 -LinkedHashSet:哈希表+单向链表,可以维护顺序 -TreeSet:底层是树结构,作用:用来排序 要求:同一种类型,要使用Comparable或Comparator,排序

2、使用场景: 集合用:ArrayList,需要大频率的增删插,将ArrayList转换为LinkedList 需要去重,将ArrayList转换HashSet,如果需要保证顺序,转换为LinkedHashSet 需要排序,将ArrayList转换为TreeSet

集合:一次保存一个值

Map:一次保存一对值(键值对)

  • 20
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Zyangxsir

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值