Java-List、Set、数据结构、Collections

数据结构

和集合相关的数据结构

  1. 栈:先进后出(类似于AK的弹夹,先放进去的子弹后打出来)

  2. 队列:先进先出(类似于卫生间排队,先来先去上厕所)

  3. 数组:查询快、增删慢

    查询快:数组的地址是连续的,可以通过数组的首地址来找到数组并通过索引进行元素查找

    增删慢:数组的长度是固定的,增加或者删除一个元素都需要建立一个新的数组后复制原数组

  4. 链表:查询慢、增删快

    查询慢:每次查询元素都是要从头开始查询

    增删快:链表结构,增加或者删除一个元素对链表的整体结构没有影响

    • 链表中的每一个元素也叫做一个节点,一个节点又包含了一个数据源(存储数组)和两个指针域(存储地址)
  5. 红黑树

    二叉树、排序树(在二叉树的基础上元素有大小顺序:左子树小、右子数大)、平衡树(左孩子和右孩子的数目是相等的)、不平衡树(左右孩子不相等)

    红黑树:特点趋近于平衡树,查询的速度非常快,查询叶子节点最大次数和最小次数不能超过2倍

    约束:

    • 节点可以是红色的或者是黑色的
    • 根节点是黑色的
    • 叶子节点(空节点)是黑色的
    • 每个红色的节点的子节点都是黑色的
    • 任何一个节点到其每一个叶子节点的所有路径上黑色节点数相同

List集合

特点:

  1. 存取有序的集合。存元素的顺序是11、22、33,取11、22、33
  2. 带有索引的集合,通过索引就可以精确的操作集合中的元素(与数组的索引是一个道理)。
  3. 集合中可以有重复的元素,通过元素的equals方法,来比较是否为重复的元素。

四种常用方法:

  • public void add(int index, E element): 将指定的元素,添加到该集合中的指定位置上。
  • public E get(int index):返回集合中指定位置的元素。
  • public E remove(int index): 移除列表中指定位置的元素, 返回的是被移除的元素。
  • public E set(int index, E element):用指定元素替换集合中指定位置的元素,返回值的更新前的元素。

常见方法的使用以及三种不同的遍历方式:

package Demo05;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

public class DemoList {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("a");
        list.add("b");
        list.add("c");
        list.add("d");
        list.add("a");  //允许重复

        //在b、c之间插入Hello
        list.add(2,"Hello");

        //返回指定索引的值
        String i = list.get(0);
        System.out.println(i);  //a

        //移除索引值为3的元素(c),返回值是这个被移除的元素
        String b = list.remove(3);
        System.out.println(b);  //cc

        //将索引为3的元素替换为Worlcd,返回的是替换前的元素
        String c = list.set(3,"World");
        System.out.println(c);  //d

        System.out.println(list);   //直接打印[a, b, Hello, World, a]

        //遍历list集合
        System.out.println("==普通的for循环==");
        for (int j = 0; j < list.size(); j++) {
            System.out.println(list.get(j));
        }

        System.out.println("==使用迭代器==");
        Iterator<String> it = list.iterator();
        while(it.hasNext()){
            System.out.println(it.next());
        }

        System.out.println("==增强for循环==");
        for (String s:list) {
            System.out.println(s);
        }
    }
}
List的子类(ArrayList、LinkedList、Vector)

ArrayList集合(底层是一个数组,数组的特点查询快、增删慢,所以对于查询多的集合建议使用ArrayList,但是增删多的不建议使用)

LinkedList集合LinkedList是一个双向链表)

特点:

  1. 底层是链表结构:查询慢、增删快
  2. 包含大量操作首尾元素的方法

常用方法:

  • public void addFirst(E e):将指定元素插入此列表的开头。

  • public void addLast(E e):将指定元素添加到此列表的结尾。

  • public void push(E e):将元素推入此列表所表示的堆栈。和addFirst()作用相同

  • public E getFirst():返回此列表的第一个元素。

  • public E getLast():返回此列表的最后一个元素。

  • public E removeFirst():移除并返回此列表的第一个元素。

  • public E removeLast():移除并返回此列表的最后一个元素。

  • public E pop():从此列表所表示的堆栈处弹出一个元素。和removeFirst()作用相同

  • public boolean isEmpty():如果列表不包含元素,则返回true。

package Demo05;

import java.util.LinkedList;

public class DemoLinkedList {
    public static void main(String[] args) {
        //show01();
        //show02();
        show03();
    }

    private static void show03() {
        LinkedList<String> list = new LinkedList<>();
        list.add("a");
        list.add("b");
        list.add("c");
        list.add("d");
        list.add("e");

        // public E removeFirst():移除并返回此列表的第一个元素。
        String a = list.removeFirst();
        System.out.println(a);  // a

        // public E removeLast():移除并返回此列表的最后一个元素。
        String b = list.removeLast();
        System.out.println(b);  // e

        // public E pop() :从此列表所表示的堆栈处弹出一个元素。与removeFirst()方法作用相同
        String c = list.pop();
        System.out.println(c);  //b
    }

    private static void show02() {
        LinkedList<String> list = new LinkedList<>();
        list.add("a");
        list.add("b");
        list.add("c");

        if (!list.isEmpty()){   //判断是否为空集合
            // public E getFirst():返回此列表的第一个元素
            String first = list.getFirst();
            System.out.println(first);  // a

            // public E getLast():返回此列表的最后一个元素
            String last = list.getLast();
            System.out.println(last);   // c
        }
    }

    private static void show01() {
        LinkedList<String> list = new LinkedList<>();
        list.add("a");
        list.add("b");
        list.add("c");

        //public void addFirst(E e):将指定元素插入此列表的开头。list.push("First");push方法与addFirst相同
        list.addFirst("First"); // [First, a, b, c]
        //public void addLast(E e):将指定元素添加到此列表的结尾。等同于list.add()方法
        list.addLast("last");   // [First, a, b, c, last]

        System.out.println(list);
    }
}
Vector集合(知道有即可,不做过多了解)

底层也是数组

Set接口

  1. 不允许有重复元素
  2. 没有索引,没有带索引的方法,不能通过普通的for循环进行遍历
HashSet:

它所存储的元素是不可重复的,并且元素都是无序的(即存取顺序不一致),底层是一个Hash表结构,查询特别快

package Demo05;

import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

public class DemoSet {
    public static void main(String[] args) {
        Set<Integer> set = new HashSet<>();
        set.add(1);
        set.add(3);
        set.add(2);
        set.add(1); //不能有重复元素
        System.out.println(set);    // 存和取的顺序不一致,[1, 2, 3]

        //由于没有索引,所以通过迭代器进行遍历
        System.out.println("==迭代器循环遍历==");
        Iterator<Integer> it = set.iterator();
        while(it.hasNext()){
            System.out.println(it.next());
        }
        
        //使用增强for循环进行遍历
        System.out.println("==增强for循环遍历==");
        for (Integer i : set){
            System.out.println(i);
        }
    }
}

哈希值:是一个十进制的整数,由系统随机给出(就是对象的逻辑地址值,但是是模拟出来的,不是数据实际存储的地址

hashCode()可以重写,没有重写系统给多少就是多少,重写了你想返回多少就多少

package Demo05;

public class DemoPerson_HashCode {
    public static void main(String[] args) {
        Person person1 = new Person(18,"迪丽热巴");
        Person person2 = new Person(20,"古力娜扎");

        int HashCode1 = person1.hashCode();
        //hashCode()可以重写,没有重写系统给多少就是多少,重写了你想返回多少就多少
        int HashCode2 = person2.hashCode();

        System.out.println(HashCode1);  //356573597
        System.out.println(HashCode2);  //1735600054
        
        System.out.println(person1);    //Demo05.Person@1540e19d
        System.out.println(person2);    //Demo05.Person@677327b6
        //356573597与1540e19d,1735600054与677327b6两两对应,一个十进制一个十六进制
    }
}
HashSet集合存储数据的结构(哈希表)

JDK1.8之前,哈希表底层采用数组+链表实现,即使用链表处理冲突,同一hash值的链表都存储在一个链表里。但是当位于一个桶中的元素较多,即hash值相等的元素较多时,通过key值依次查找的效率较低。

而JDK1.8中,哈希表存储底层采用数组+链表+红黑树实现,简单来说就是数组用来存放哈希值,然后数组的下方加入链表,具有相同哈希值的元素放在链表中;当链表长度超过阈值(8)时,将链表转换为红黑树,这样大大减少了查找时间。

set集合存储元素不重复的原理:
Set<String> set = new HashSet<>();
set.add("abc");
set.add("重地");
set.add("通话");
set.add("abc");
System.out.println(set);	//[重地, 通话, abc]
  1. set集合在调用add方法时会调用元素的hashCode方法和equals方法判断是否重复

  2. 当加入字符串“abc”时,add方法会先调用hashCode方法生成一个哈希值------96354,然后在集合当中寻找有没有这个哈希值在,当发现没有的时候,就会把“abc”存入集合当中

  3. 当第二个“abc”加入的时候,也会产生哈希值------96354,但是这个时候集合中已经有这个哈希值了,这个时候就会调用euqals方法将两个元素进行比较,返回true,发现两个元素一样,就不会存入第二个“abc”

  4. 而对于“重地”和“通话”,存入“重地”后,会有一个哈希值为------1179395的元素,当存入“通话”时,产生哈希冲突,这个时候同样调用equals方法,发现两个元素不同,就会把元素“通话”存入

    重点是:先调用hashCode方法,产生哈希值然后判断,是否存在相同哈希值的元素,不存在,直接存入;如果存在,则再调用equals方法,判断两个元素是否相同,不同则存入,相同则不存

HashSet存储自定义类型元素

在使用系统自定义的hashCode和equals方法时会出现存入哈希值相同的情况,例如“重地”和“通话”,如果需要存放入自定义的类型元素时,就需要重写hashCode和equals方法,建立自己的比较方式,保证HashSet集合中的对象唯一

package Demo05;

import java.util.HashSet;

public class HashSetSelf {
    public static void main(String[] args) {
        HashSet<HashSetSelfPerson> set = new HashSet<>();
        HashSetSelfPerson person1 = new HashSetSelfPerson("迪丽热巴",18);
        HashSetSelfPerson person2 = new HashSetSelfPerson("迪丽热巴",18);
        HashSetSelfPerson person3 = new HashSetSelfPerson("迪丽热巴",19);
        System.out.println(person1.hashCode());     //重写前:356573597     后:289834393
        System.out.println(person2.hashCode());     //重写前:1735600054    后:289834393

        set.add(person1);
        set.add(person2);
        set.add(person3);

        System.out.println(set);
    }
}
package Demo05;

import java.util.Objects;

public class HashSetSelfPerson {
    private String name;
    private int age;

    public HashSetSelfPerson() {
    }

    public HashSetSelfPerson(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public String toString() {
        return "HashSetSelfPerson{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    //重写的equals方法
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        HashSetSelfPerson that = (HashSetSelfPerson) o;
        return age == that.age &&
                Objects.equals(name, that.name);
    }

    //重写的hashCode方法
    @Override
    public int hashCode() {
        return Objects.hash(name, age);
    }
}

LinkedHashSet

HashSet保证元素唯一,可是元素存放进去是没有顺序的,那么我们要保证有序,怎么办呢?

在HashSet下面有一个子类java.util.LinkedHashSet,它是链表和哈希表组合的一个数据存储结构。

LinkedHashSet底层是一个哈希表,保证元素有序。可以有序输出,但是仍不允许重复

package Demo05;

import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.Set;

public class DemoLinkedHashSet {
    public static void main(String[] args) {
        Set<String> set = new LinkedHashSet<String>();
        set.add("bbb");
        set.add("aaa");
        set.add("abc");
        set.add("bbc");
        Iterator<String> it = set.iterator();
        while (it.hasNext()) {
            System.out.println(it.next());
        }
    }
}
结果:
  bbb
  aaa
  abc
  bbc

可变参数

当方法的参数列表数据已经确定,但是参数的个数没有确定时,就可以使用可变参数

格式:修饰符 返回值类型 方法名(参数类型... 形参名){ }

底层是一个数组:根据传入参数的不同,会创建不同长度的数组,来存储这些参数

package Demo05;

public class DemoVarArgs {
    public static void main(String[] args) {
        int i = add(10,20,30,40,50);  //add(x),x传递几个数据,就创建多少长度的数组
        System.out.println(i);	//150
        int j = add(10,20);
        System.out.println(j);	//30
    }

    /*
        计算(0-n)整数方法的和
        已知:计算整数的和,数据类型已经知道了,但是数据的个数不清楚
         */
    public static int add(int...arr){
        int sum = 0;
        for (int i:arr) {
             sum = sum + i;
        }
        return sum;
    }
}

注意事项:

  1. 一个方法的参数列表,只能是有一个可变参数

    public static int add(int...arr,String ...b){}	//不行
    
  2. 如果方法的参数有多个,那么可变参数必须卸载参数列表的末尾

    public static int add(int a,int b,String c,int...arr){}	//可变参数在末尾
    

Collections

常用功能:

  • java.utils.Collections是集合工具类,用来对集合进行操作。部分方法如下:
  • public static <T> boolean addAll(Collection<T> c, T... elements):往集合中添加一些元素。

  • public static void shuffle(List<?> list) 打乱顺序:打乱集合顺序。

    package Demo05;
    
    import java.util.ArrayList;
    import java.util.Collections;
    
    public class DemoCollections {
        public static void main(String[] args) {
            ArrayList<String> list = new ArrayList<>();
            ArrayList<String> list1 = new ArrayList<>();
            list.add("a");
            list.add("b");
            list.add("c");
            list.add("d");
            list.add("e");
    
            Collections.addAll(list1,"a","b","c","d","e");  //添加
            System.out.println(list);   //[a, b, c, d, e]
            System.out.println(list1);  //[a, b, c, d, e]
    
            Collections.shuffle(list);  //打乱顺序
            System.out.println(list);   //[e, b, d, a, c]
        }
    }
    
  • public static <T> void sort(List<T> list):将集合中元素按照默认规则(升序)排序

  • public static <T> void sort(List<T> list,Comparator<? super T> ):将集合中元素按照指定规则排序。

    package Demo05;
    
    import java.util.ArrayList;
    import java.util.Collections;
    
    /*
    Comparable排序规则
    this-参数(升序)
    参数-this(降序)
     */
    
    public class DemoCollections {
        public static void main(String[] args) {
            ArrayList<Integer> list = new ArrayList<>();
    
            Collections.addAll(list,2,4,7,5,1);  //添加
            System.out.println(list);   //[2, 4, 7, 5, 1]
    
            Collections.sort(list);     //默认升序排列
            System.out.println(list);   //[1, 2, 4, 5, 7]
    
            ArrayList<String> list1 = new ArrayList<>();
    
            Collections.addAll(list1,"a","e","b","d","c");
            System.out.println(list1);  //[a, e, b, d, c]
    
            Collections.sort(list1);
            System.out.println(list1);  //[a, b, c, d, e]
    
            ArrayList<Person> list2 = new ArrayList<>();
            list2.add(new Person(18,"张三"));
            list2.add(new Person(20,"李四"));
            list2.add(new Person(16,"王五"));
            System.out.println(list2);  //[Person{age=18, name='张三'}, Person{age=20, name='李四'}, Person{age=16, name='王五'}]
    
            Collections.sort(list2);
            System.out.println(list2);
        }
    }
    
    
    package Demo05;
    
    public class Person implements Comparable<Person>{
        private int age;
        private String name;
    
        public Person() {
        }
    
        public Person(int age, String name) {
            this.age = age;
            this.name = name;
        }
    
        @Override
        public String toString() {
            return "Person{" +
                    "age=" + age +
                    ", name='" + name + '\'' +
                    '}';
        }
    
        public int getAge() {
            return age;
        }
    
        public void setAge(int age) {
            this.age = age;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        @Override
        public int hashCode() {
            return 1;
        }
    
        @Override
        public int compareTo(Person o) {
            return this.getAge()-o.getAge();
        }
    }
    

简述Comparable和Comparator两个接口的区别。

Comparable:强行对实现它的每个类的对象进行整体排序。这种排序被称为类的自然排序,类的compareTo方法被称为它的自然比较方法。只能在类中实现compareTo()一次,不能经常修改类的代码实现自己想要的排序。实现此接口的对象列表(和数组)可以通过Collections.sort(和Arrays.sort)进行自动排序,对象可以用作有序映射中的键或有序集合中的元素,无需指定比较器。

Comparator强行对某个对象进行整体排序。可以将Comparator 传递给sort方法(如Collections.sort或 Arrays.sort),从而允许在排序顺序上实现精确控制。还可以使用Comparator来控制某些数据结构(如有序set或有序映射)的顺序,或者为那些没有自然顺序的对象collection提供排序。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值