Java基础--集合


集合

目录
  • 集合
    • Collection接口
      • List接口:不唯一,有序
        • ArrayList实现类
        • vector实现类(已淘汰)
        • LinkList实现类
      • Set接口:唯一,无序(无序不是随机)
        • Set接口:唯一,无序(无序不是随机)
        • LinkedHashSet实现类
        • TreeSet实现类
    • Map接口
      • HashMap实现类
      • TreeMap实现类

当需要将相同结构的个体整合到一起的时候,需要集合。

Collection接口

注意!!!集合存入的都是引用数据类型,不能存基本数据类型

常用方法:

增 add(e) addAll(collection c)
删 clear() remove(Object o )
查 size() ietrator()
判断:contains(Object o)  equals(Object o)  isEmpty()
//接口不能创建对象,用它的实现类创建对象
Collection c1 = new ArrayList();
//集合只能存放引用数据类型的数据,不能存基本数据类型
//基本数据类型自动装箱,转换成对应的包装类  int--->Integer
c1.add(18);  //添加元素
c1.add(12);
c1.add(17);
System.out.println(c1.toString());    //输出[18, 12, 17]

 //addAll(Collection c) 传入一个集合名字,将集合c添加到本集合中
        List list = Arrays.asList(new Integer[]{23,25,29,36});
        c1.addAll(list);
        System.out.println(c1.toString());  //输出[18, 12, 17, 23, 25, 29, 36]

//equals比较的是两个集合中的值是否对应相等,==比较的是两个集合的地址
//contains判断集合是否包含某元素
System.out.println("是否包含9:"+c1.contains(9));

遍历方式:

方法一:增强for

for (Object o:c1)   //(用什么接收:要遍历哪个)
      { System.out.println(o);
}

方法二:迭代器iterator

Iterator it = c1.iterator();  //把迭代器看作一个“指针”
while(it.hasNext()){     //通过hashNex()判断是否有下一个元素,返回true
    System.out.println(it.next());  //next()将元素获取到,“指针”下移
}

List接口:不唯一,有序

常用方法:扩展的方法都和索引相关

//list接口常用方法-是collection接口的子接口,继承了collection,还有一些通过索引来操作的
//增 add(int index, E element)
//删 remove(int index)  remove(Object o)
//改 set(int index,E element)
//查 get(int index)
List list = new ArrayList();  //接口不能创建实例,用实现类创建
list.add(13);
list.add(1);
list.add(3);
list.add(130);
list.add("zxy");
System.out.println(list);
list.add(3,33);    //下标从0开始,在下标为3的位置添加33
list.set(3,77);   //把下标为3的数改成77
list.remove(4);     //把下标为4的删除
System.out.println(list); //若在集合中存的是Integer类型数据,调用哪个remove都可以
list.remove("zxy");    //若是其他类型,必须调用remove(Object o)才能删除
//通过索引获取元素
  Object o = list.get(5);  //集合里有多种类型,要用Object类型接收
  System.out.println(o);

遍历方法:

//方式一:普通for循环
System.out.println("===========================");
for (int i = 0; i 
//方式二:增强for循环
System.out.println("===========================");
for (Object value : list) {
    System.out.println(value);
}
//方式三:迭代器
Iterator it = list.iterator();
while(it.hasNext()){
    System.out.println(it.next());
}
ArrayList实现类

ArrayList的源码与StringBuider极为相似,都有两个重要的属性:ArrayList是Object[]型数组,int size指代数组中的有效长度

而StringBuider中是char[] value--底层的存储数组, int count--数组中的有效长度

JDK1.8源码:底层是数组,在调用构造器时,底层数组为{},在调用了add()方法后,底层数组才重新赋值为新数组,长度为10-->节省了内存

数据结构:

  • 物理结构:紧密结构(顺序结构)
  • 逻辑结构:线性表(数组)
vector实现类(已淘汰)

JDK1.8源码:底层Object类型数组,int类型属性表示有效长度

vector与ArrayList的联系与区别:

联系:底层都是数组的扩容

区别:1)ArrayList底层扩容长度为原数组的1.5倍,线程不安全,效率高

? 2)vector底层扩容长度为原数组的2倍,线程安全,效率低 (已淘汰 )

  • 什么是泛型:相当于标签--实际就是一个<>引起来的参数类型,在使用时才会确定具体类型

  • 没有泛型时:集合中任何的引用类型都能存,有多种类型的,不太方便。

  • 加入泛型:在编译时期会对类型进行检查,不是对应的类型就不会加入这个集合。此时集合中是同一类型

  • 使用了泛型,后序遍历操作简单;泛型一般都是用来修饰集合,所以泛型的类型都是引用数据类型,不能是基本数据类型

  • ArrayList a = new ArrayList<>();
    

---泛型类

//test04就是一个普通类,而test04就是个泛型类
//<>里面是一个参数类型,但这个类型现在不确定,相当于一个占位符,
// 唯一确定的是他一定是一个引用数据类型
    public class test04 {
    int age;
    String name;
    E sex;
    public void a(E n){ }
    public void b(E[] m){ }}
public static void main(String[] args) {
   //实例化泛型类
    //实例化时不指定泛型,默认此泛型为Object类型
     test04 t = new test04();
     t.a("zxy");
     t.a(2);
     t.b(new String[]{"a","b"});
     //实例化时指定泛型!!!
    test04 t1 = new test04<>();  //确定了泛型为String类型,故上面的E对应String
    t1.sex = "男";
    t1.a("abc");

继承情况:

1)父类指定泛型,子类就不必指定

class subtest04 extends test04{
}

2)父类不指定泛型,子类也会变成一个泛型类,E的类型可以在创建子类对象时确定

class subtest004 extends test04{
}

细节:

1)泛型类可以定义多个参数

2)泛型类对应的构造器--不可以加泛型<>

3)不同的泛型的引用类型不可以相互赋值

public void c(){
    ArrayList list1 = null;
    ArrayList list1 = null;
    list1 = list2;    //错误代码,不可以相互赋值
}

4)泛型如果不指定,用的时候就会默认为Object类型

5)泛型类中的静态方法不能使用类的泛型

public static void b(E[] m){ }  //报错,静态方法在最开始就会被加载,而泛型在实例化后才能确定

6)不能直接使用E[]创建:e[] i = new e[10]; //不可以

---泛型接口:类似于泛型类

---泛型方法:这个方法的泛型参数类型 与 当前所在类 是否是泛型类,泛型是啥 无关!

? 定义时要加上,不加的话会把T看作一种数据类型,会报错

? T 的类型是在调用的时候才确定的(根据传入的参数类型),所以泛型方法可以是静态方法

public class test05  {
    public void a(E e){ }      //不是泛型方法
    public  void b(T t){ }  //是泛型方法
    public static  void b(T t){ }  //是泛型方法
}

---泛型参数存在继承关系

Object obj = new Object();
String s = new String();
obj = s;   //多态的一种形式---父类的引用指向子类

Object[] objArr = new Object[];
String[] strArr = new String[];
objArr = strArr;  //多态的一种形式

List list1 = new ArrayList<>();
List list2 = new ArrayList<>();
list1 = list2;   //报错--ArrayList底层都是Object数组,利用泛型给他加限制,只是在编译时限制,
                 // 所以list1和list2是并列关系

总结:A和B是子类父类关系,但G和G不存在继承关系,是并列的

---通配符 <?>

  • 在没有通配符时,下面相当于方法的重复定义,报错

    public void a(List list){
    }
    public void a(List list){
    }
    public void a(List list){
    }
    
    
    
  • 引入通配符后:G<?>成为了GG的父类

    public void a(List<?> list){
        for (Object o:list) {     //遍历时用Object接收
            System.out.println(o);
        }
    }
    
    //引入通配符后,传什么类型都可以
    public static void main(String[] args) {
       test07 t = new test07();
       t.a(new ArrayList());
       t.a(new ArrayList());
       t.a(new ArrayList());
    
    
    
    
    
            //数据的写入操作
            //list.add("abc"); 不能随意添加数据,只能填null
            list.add(null);
            //数据的读取操作--用Object类型接收
            Object s = list.get(0);
    

    泛型受限:List<? extends Person>

           //使用泛型受限--上限,也就是说Person是天花板
            List<? extends Person> list1 = null;
    // 报错       list1 = a;
            list1 = b;   //只能是Person类或者他的子类
            list1 = c;
    
            //使用泛型受限--下限,也就是说Person是地板
            List<? super Person> list2 = null;
            list2 = a;
            list2 = b;   //只能是Person类或者他的父类
    // 报错       list1 = c;
    
    LinkList实现类

    常用方法:

    LinkList常用方法:
    增 addFirst(E e) addLast(E e)
    offer(E e) offerFirst(E e) offerLast(E e)
    删  poll() pollFirst() pollLast() //poll是JDK1.6之后的,代码健壮性好
    removeFirst()  removeLast()
    查 element() getFirst() getLast()
     indexOf(Object o) lastIndexOf(Object o)
     peek() peekFirst() peekLast()
    
     //创建一个对象
            LinkedList list = new LinkedList<>();
            list.add("zzzzzz");
            list.add("xxxxxxx");
            list.add("yyyyyyy");
            list.add("zzzzzz");  //可以添加重复数据
    
            list.addFirst("hh"); //向头、尾添加
            list.addLast("gg");
            list.offer("aa");   //添加到尾部
            list.offerFirst("bb"); //头部
            list.offerLast("cc");  //尾部
    
            list.poll();  //删除头部元素 pollFirst()
            list.pollLast(); //删除尾部元素
            list.clear();  //清空
            System.out.println(list);
    
            System.out.println(list.pollFirst());   //集合为空,返回null
            System.out.println(list.removeFirst());  //若集合为空,报错了
    

    遍历方法

    实现List接口,和他一样有三种遍历方式。。。

    迭代器遍历有另一种写法:更节省内存

    Iterator it = list.iterator();
    while(it.hasNext()){
    System.out.println(it.next());}
    
    for(Iterator it = list.iterator(); it.hasNext();){
        System.out.println(it.next());
    }
    

    底层原理:

    数据结构:

    • 物理结构:跳转结构(链式结构)
    • 逻辑结构:线性表(链表)
    • LinkedList底层是是双向链表 --一个节点包含三部分:前一个元素地址+当前存入的元素+后一个元素地址

    Set接口:唯一,无序(无序不是随机)

    遍历:迭代器,增强for循环

    Set接口:唯一,无序(无序不是随机)

    方法:add(),clear(),remove()...

    HashSet hs = new HashSet<>();
    System.out.println(hs.add(10));  //true
    hs.add(0);
    hs.add(15);
    System.out.println(hs.add(10));  //false,这个10没有返回到集合中
    hs.add(12);
    System.out.println(hs.size()); //唯一,不存在重复元素
    System.out.println(hs);
    
    HashSet ts = new HashSet<>();   //自定义数据类型--不满足唯一
    ts.add(new test10.Student("lili",20,"男"));
    ts.add(new test10.Student("ll",15,"女"));
    ts.add(new test10.Student("lili",20,"男"));
    ts.add(new test10.Student("li",200,"女"));
    System.out.println(ts.size());  //4
    //在自定义的类型中,重写hashCode方法和equals方法就可以达到唯一
    

    底层原理

    哈希表=数组+链表

    注意:如果放入HashSet中的数据,一定要重写两个方法:hashCode,equals

    LinkedHashSet实现类

    特点:唯一,有序--按照输入顺序进行输出

    底层:在HashSet的基础上多了一个总链表,把元素按照顺序链接起来,方便有序遍历

    插入知识点:比较器

    1. 比较Int类型数据:将比较的数据作差,然后返回一个int类型的数据,将这个int类型的数值 按照 =0 >0 <0

    2. 比较String类型数据:String类实现了Comparable接口,这个接口中有一个抽象方法compareTo,String类中重写这个方法即可。

      public static void main(String[] args) {
          String a = "A";
          String b = "B";
          System.out.println(a.compareTo(b));   //-1
          //String实现了Comparable接口,Comparable里有抽象方法compareTo,在String中重写了这个方法
      }
      
    3. 比较double类型数据:将数据包装成Double类,调用compareTo方法

      double c = 9.987;
      double d = 8.876;
      System.out.println(((Double)c).compareTo((Double)d));
      
    4. 比较自定义的数据类型:

      1)内部比较器(实现Comparable接口)--每次只能比较一个(在类里面写)

        @Override
          //实现Comparable接口必须重写compareTo方法
          public int compareTo(Student o) {
              //比较年龄
      //        return this.getAge() - o.getAge();
              //比较身高
      //        return ((Double)(this.getHeight())).compareTo((Double)(o.getHeight()));
              //比较名字
              return this.getName().compareTo(o.getName());
          }
      
      public static void main(String[] args) {
          Student s1  = new Student(16,165.2,"nana");
          Student s2 = new Student(18,160.6,"lala");
          System.out.println(s1.compareTo(s2));
      }
      

      2)外部比较器--更好,多态,扩展性好

      在类之外写外部比较器(实现 Comparator接口 )--可以同时比较多个

      public class Student1 {
       ... 
      }
      class biJiao1 implements Comparator {
          @Override
          //重写了Comparator接口中的compare方法
          public int compare(Student o1, Student o2) {
              //比较年龄
              return o1.getAge() - o2.getAge();
          }
      }
      class biJiao2 implements Comparator {
          @Override
          public int compare(Student o1, Student o2) {
              //比较身高
              return ((Double) (o1.getHeight())).compareTo((Double) (o2.getHeight()));
          }
      }
      
          //获取外部比较器--多态
              Comparator bj1 = new biJiao1();
              System.out.println(bj1.compare(s1,s2));
      
              Comparator bj2 = new biJiao2();
              System.out.println(bj2.compare(s1,s2));
      
    TreeSet实现类
    1. 存入Integer类型数据:底层是内部比较器,唯一,有序(按升序进行遍历),无序(不是按照输入顺序)

    2. 存入String类型数据--底层实现类内部比较器

    3. 放入自定义的Student类型数据--可以利用内部比较器--也可以利用外部比较器

    4. 原理:底层-二叉树(逻辑结构)-物理结构:跳转结构(链式结构)

      在数中放入数据时,最重要的是比较的实现-->内部比较器或者外部比较器

      底层的二叉树遍历是中序遍历得到的,即按照升序结果出现

    Map接口

    HashMap实现类

    底层原理:数组+链表+红黑树(java8新增红黑树)

    TreeMap实现类