集合

数组的缺点
语法: 动态数组:数据类型[] 数组名=new 数据类型[lenght];

静态数组:数组类型[] 数组名={元素1,元素2,元素3,元素4,元素5......};

缺点: 存放的元素类型固定。而且数组的长度固定的。我们在实际开发中存放元素的个数可能不确定,这时就无法使用数组。
手动编写一个可扩展的数组类

import java.util.Arrays;

public class MyArray {
    private Object[] arr;//声明了一个数组
    private int size=0;//表示数组的下标索引。

    public MyArray() {
        arr=new Object[10];//在无参构造中,给一个默认值。。。。
    }

    public MyArray(int length) {
        if(length<0){
            throw new RuntimeException("你输入的长度有误!");//长度必须大于0
        }
        arr = new Object[length];//创建数组
    }
    public void add(Object o){
            if(size>= arr.length){//判断数组是否已放满,这里用==也可
                Object[] tmp=new Object[arr.length*2];//将数组扩容
                for (int i = 0; i < arr.length; i++) {
                    tmp[i]=arr[i];//老数组的值赋值给新数组
                }
                arr=tmp;//数组名改回原来数组名
            }//完成扩容
            arr[size]=o;//为数组添加数值
            size++;//记录数组中数据的个数
    }
    public Object get(int index){
        if(index<0||index>= arr.length){//判断索引是否越界
            throw new RuntimeException("数组下标越界...");
        }
        return arr[index];//返回位置数值
    }
    public int Size(){
        return size;//数组存入数据的个数
    }

    @Override
    public String toString() {
        return "MyArray{" +
                "arr=" + Arrays.toString(arr) +
                '}';
    }
}
  1. 我们上面自己手动编写了一个可扩展的数组类,那么该类就无限的往里面添加元素。
    我们都能相同自己编写一个可扩展的数组类,那么java开发人员,它们也一定能想到。
  2. java开发员根据不同的数据结构,比如 集合 线性结构 队列 映射结构等而开发出来的类称为集合框架。
    集合框架

    Vector
    public class Vector

extends AbstractList

implements List, RandomAccess, Cloneable, Serializable

Vector类实现了可扩展的对象数组。 像数组一样,它包含可以使用整数索引访问的组件。 但是, Vector的大小可以根据需要增长或缩小,以适应在创建Vector之后添加和删除元素

与新集合实现不同, Vector是同步的。 如果不需要线程安全的实现,建议使用ArrayList代替Vector 。

面试: ArrayList和Vector的区别?

Vector是同步。而ArrayList是异步的。

创建Vector类的对象

   @Test

    public void test01(){

        Vector v=new Vector();//创建Vector对象默认10

        Vector v2=new Vector(12);//创建Vector对象并指定数组的大小

    }

增删改查
增添

import org.junit.Test;

import java.util.Vector;

public class demo02 {
    @Test
    public void test01(){
  Vector v=new Vector();//Vector默认空间为10
        //增加元素
        v.add(1);
        v.add("2");
        v.add(1.1);
        v.add('4');
        v.add(1.1f);
        v.add(null);
        v.add(true);
        v.add("小木不痞");
        Vector v1=new Vector(2);
        v1.add("hello");
        v1.add("hello");
        v.addAll(v1);//将v1中的元素一个一个放进集合。
        v.add(v1);//将一整个集合v1放进v中
        //因为Vector为Object数据类型,所以这里的Vector中基本什么数据类型都可以放,
        v.add(1,"小木"); //原本在位置1上添加元素。 1原来的元素都要后移。
        System.out.println(v);       Vector v=new Vector();//Vector默认空间为10
        //增加元素
        v.add(1);
        v.add("2");
        v.add(1.1);
        v.add('4');
        v.add(1.1f);
        v.add(null);
        v.add(true);
        Vector v1=new Vector(2);
        v1.add("hello");
        v1.add("hello");
        v.add(v1);
        //因为Vector为Object数据类型,所以这里的Vector中基本什么数据类型都可以放,
        v.add(1,"小木"); //原本在位置1上添加元素。 1原来的元素都要后移。
        System.out.println(v);
    }
}

修改

//修改
v.set(1,"小木不痞");//对象名.set(索引,改后内容);
System.out.println(v);

删除

//删除
v.remove("小木不痞");//根据内容删除,只删索引靠前的内容
System.out.println(v);
v.remove(1);//根据索引删除,如果索引和内容一样优先默认删除索引位置内容
v.remove((Integer)1);//想要删除数字的话,需要对数字进行转型
System.out.println(v);
v.clear();//清空
System.out.println(v);

查询

Object o=v.get(1);//查询索引为1位置上的内容
System.out.println(o);
int size=v.size();//查询数组长度
System.out.println(size);
boolean xiaomu=v.contains("小木不痞");//判断是否在集合中存在此内容。
System.out.println(xiaomu);
boolean empty=v.isEmpty();//判断集合是否为空
System.out.println(empty);
for (int i = 0; i < v.size(); i++) {
    Object o1=v.get(i);
    System.out.println(o1);

ArrayList集合
public class ArrayList

extends AbstractList

implements List, RandomAccess, Cloneable, Serializable

可调整大小的数组的实现List接口。 实现所有可选列表操作,并允许所有元素,包括null 。 除了实现List 接口之外,该类还提供了一些方法来操纵内部使用的存储列表的数组的大小。 (这个类是大致相当于Vector,不同之处在于它是不同步的)。

添加元素

import org.junit.Test;

import java.util.ArrayList;

public class demo03 {
    @Test
    public void testAdd(){
        //对象集合容器,改容器只能储存对象。
        ArrayList list=new ArrayList();
        list.add("hello");
        list.add(12);//12(int)---->自动装箱为(Integer),然后放进ArrayList中
        list.add(null);
        list.add(1,"word");//规定添加位置,规则和Vector一样。
        ArrayList list1=new ArrayList();
        list1.add("小木");
        list1.add("不痞");
        list.add(list1);//把一个集合中的元素添加到当前集合中。
        System.out.println(list);
    }
}

修改

//修改
@Test
public void testUpdate(){
    ArrayList list=new ArrayList();
    list.add("java1");
    list.add("java2");
    list.add("java3");
    list.add("java4");
    list.add("java5");
    list.add("java6");
    list.add("java7");
    list.add("java8");
    list.add("java9");
    list.add("java10");
    list.set(1,"小木");//修改下标为1位置的内容
    System.out.println(list);
    List list1=list.subList(1,7);//截取集合,不能超范围,超出会出现异常...
    System.out.println(list1);
}

删除

//删除
@Test
public void testDelete(){
    ArrayList list=new ArrayList();
    list.add("java1");
    list.add("java2");
    list.add("java3");
    list.add("java4");
    list.add("java5");
    list.add("java6");
    list.add("java7");
    list.add("java8");
    list.add("java9");
    list.add("java10");
    list.remove("java03");//根据内容移除.找不到就不删,也不会报错。。。
    list.remove(0);//根据下标移除
    System.out.println(list);
    ArrayList list1=new ArrayList();
    list1.add("java10");
    list1.add("java");
    list.removeAll(list1);//根据内容移除.找不到就不删,也不会报错,集合中删集合所有此集合都删完,有几个删几个。
    System.out.println(list);
}

查询

@Test
public void testSelect(){
    ArrayList list=new ArrayList();
    list.add("java1");
    list.add("java2");
    list.add("java3");
    list.add("java4");
    list.add("java5");
    list.add("java6");
    list.add("java7");
    list.add("java8");
    System.out.println(list.get(1));
    System.out.println(list.size());
    Object[] objects=list.toArray();//将集合转化为数组
    System.out.println(list.contains(10));//查询是否在数组中存在
    System.out.println(list);
}

遍历

@Test
public void test(){
    ArrayList list=new ArrayList();
    list.add("java1");
    list.add("java2");
    list.add("java3");
    list.add("java4");
    list.add("java5");
    list.add("java6");
    list.add("java7");
    list.add("java8");
    System.out.println("下标遍历");
    for (int i = 0; i < list.size(); i++) {
        System.out.println(list.get(i));
    }
    System.out.println("增强for循环");
    for(Object o:list){
        System.out.println(o);
    }
}

ArrayList集合底层源码
1.我们先从ArrayList的构造函数看;

 public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }


从上面的代码我们分析出来: elementData={}; 创建了一个长度为0的空数组。
2.分析添加的源码
如;arrayList.add("java");
add底层源码如下:

     //其中的E指的是:泛型,我们可以先把它理解为Object类型
    public boolean add(E e) {
        ensureCapacityInternal(size + 1);  //判断数组是否已满,并进行扩容
        elementData[size++] = e;//将元素赋值给集合的size++位置
        return true;
    }

// 扩容代码:ensureCapacityInternal();
    private void ensureCapacityInternal(int minCapacity) {
        ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
    }

    private void ensureExplicitCapacity(int minCapacity) {
        modCount++;

        // 当最小容量-elementData.length>0,说明存储空间满了,需要扩容了。
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);//调用扩容方法
    }
   //grow()核心内容
   private void grow(int minCapacity) {
        // 老数组的长度
        int oldCapacity = elementData.length;
        //>>表示位运算符:这里表示*0.5倍,所以新数组的长度为老数组的1.5倍。
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        //判断新数组长度见最小数组长度是否小于0.
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;//成立将最小数组长度赋给新数组。
        if (newCapacity - MAX_ARRAY_SIZE > 0)//MAX_ARRAY_SIZE=Integer.MAX_VALUE - 8
            newCapacity = hugeCapacity(minCapacity);//在去调方法
        //将老数组内容赋给新数组并扩容1.5倍,最后回来赋值给老数组名。
        elementData = Arrays.copyOf(elementData, newCapacity);
    }
private static int hugeCapacity(int minCapacity) {
        if (minCapacity < 0) // 判断是否数据溢出
            throw new OutOfMemoryError();//溢出显示异常
        return (minCapacity > MAX_ARRAY_SIZE) ?
            Integer.MAX_VALUE :
            MAX_ARRAY_SIZE;//没有溢出现象就返回新数组长度
    }

从上面的源码我们分析出ArrayList底层使用的数组结构【特点: 查询效率块。中间删除和添加效率慢】,而且数组在进行扩容时,是原来的1.5倍。并且使用Arrays.copyof方法进行扩容并把原数组的内容赋值过来。
LinkedList
它的底层使用链表结构,特点:便以删除和添加操作。但是它的查询效率慢

    @Test
    //使用LinkedList
    public void test02(){
        LinkedList list=new LinkedList();
        //添加
        list.add("java02");
        list.addLast("不痞");//最后位置添加
        list.addFirst("java01");//第一个位置添加
        list.add(2,"小木");//指定位置添加
        System.out.println(list);
        list.set(0,"java1");
        list.add(2,"java03");
        list.add(3,"java04");
        list.add(4,"java05");
        list.add(5,"java06");
        System.out.println("list = " + list);
        //查询
        System.out.println("list.get(1) = " + list.get(1));//指定位置内容
        System.out.println("list.isEmpty() = " + list.isEmpty());//判断集合是否为空。
        System.out.println("list.getFirst() = " + list.getFirst());//集合第一个元素内容
        System.out.println("list.getLast() = " + list.getLast());//集合最后一个元素内容。
        System.out.println("list.contains(\"java01\") = " + list.contains("java01"));//查询内容是否存在
        System.out.println("list.indexOf(\"xiaomu\") = " + list.indexOf("xiaomu"));//查询数据下标,不存在返回-1
        //删除
        list.remove();//默认删除第一个数
        list.removeFirst();//和remove默认功能一样
        System.out.println(list);
        list.remove("小木");
        list.removeLast();//删除最后一个内容
        list.clear();//清空
        System.out.println(list);
    }

LinkedList底层源码

    //构造方法:
    transient int size = 0;
    transient Node<E> first;
    transient Node<E> last;
    public LinkedList() {
    }
    //发现: LinkedList类中存在三个属性: size:元素的个数  first:表示第一个节点   last:最后一个节点
    //其中的Node类
        private static class Node<E> {
        E item;
        Node<E> next;
        Node<E> prev;
        Node(Node<E> prev, E element, Node<E> next) {
            this.item = element;
            this.next = next;
            this.prev = prev;
        }
    }//   E item;当前元素的内容
     //   Node<E> next;下一个节点
     //   Node<E> prev;上一个节点

添加属性原理




查询原理

get(index):
    public E get(int index) {
        checkElementIndex(index);//检查传入的下标是否和否
        return node(index).item;
    }
//--------------------------------------------------------------------------------
    Node<E> node(int index) {
        // 判断你传入的下标是否超过元素的一半。
        if (index < (size >> 1)) {
            Node<E> x = first;
            for (int i = 0; i < index; i++)
                x = x.next;
            return x;
        } else {
            Node<E> x = last;
            for (int i = size - 1; i > index; i--)
                x = x.prev;
            return x;
        }
    }


ArrayList和LinkedList区别
1.ArrayList:底层使用的数组,查询效率高。中间位置增加和删除元素效率低,因为设计到数组元素的位移。
扩容机制,按照老数组的1.5倍扩展并把老数组的内容复制过去。 Arrays.copyof方法扩容。
2.LinkedList底层使用的链表结构,增加和删除效率高。查询效率低,从前往后或从后往前依次遍历。
HashSet集合
特点: 里面元素无序【添加元素的顺序无关】,而且里面的元素不可重复。

 @Test
    public void test03(){
        HashSet hashSet=new HashSet();//创建一个HashSet对象
        //添加元素---发现:set和添加的顺序无关而且没有下标。set中不允许出现重复的内容。允许添加null值。
        hashSet.add("java01");
        hashSet.add("java02");
        hashSet.add("java03");
        hashSet.add("java04");
        hashSet.add("java05");
        hashSet.add("java06");
        hashSet.add(null);
        System.out.println(hashSet);
        //判断元素是否在set集合中
        System.out.println("hashSet.contains(\"java\") = " + hashSet.contains("java"));
        //遍历---因为该集合没有下标。 所以我们不能使用下标循环遍历。  foreach循环遍历jdk1.5后。
        for (Object o :
                hashSet) {
            System.out.println(o);
        }
        System.out.println("~~~~~~~~~~~~~~~~~~~~");
        //迭代器遍历
        Iterator iterator=hashSet.iterator();//获取set集合的迭代器对象。
        while (iterator.hasNext()){//判断当前指针是否可以移动到下一位。
            Object next= iterator.next();//指针移到下一位,并获取该位上的内容。
            System.out.println("next = " + next);
        }
        //删除只能根据内容删除
        hashSet.remove("java06");
        hashSet.clear();
    }

HashSet底层原理

import java.util.Objects;

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

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Student student = (Student) o;
        return age == student.age && Objects.equals(name, student.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name, age);
    }

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

}

hashset判断元素是否重复的底层使用的是Hashcode方法和equals方法。
原理:如果对象的中的equals为false,则将值放进集合中,如果为true,则判断hash值是否相同,如果不相同,则将值放进集合中,如果相同则不将元素放进集合中。
TreeSet集合
我们知道HashSet是没有顺序的,而TreeSet最大的特点就是一个有顺序的去重集合容器。

  • 集合中的元素没有重复
  • 集合中的元素不保证插入顺序,而是默认使用元素的自然排序,不过可以自定义排序器
  • jdk8以后,集合中的元素不可以是null
  • 集合不是线程安全
  • 相对于HashSet, 性能更差。
    使用TreeSet
package lx;

import org.junit.Test;

import java.util.Iterator;
import java.util.TreeSet;

public class Demo02 {
    @Test
    public  void  test01(){
        TreeSet set=new TreeSet();
        //添加元素: 元素有序而且不能重复,而这里的有序并不是按照你的添加顺序。而且默认按照元素的自然排序执行。 JDK1.8后不允许添加null值
        set.add(1);
        set.add(3);
        set.add(2);
        set.add(1);
        set.add(6);
        set.add(5);
        set.add(4);
        System.out.println(set);
        System.out.println("set.size() = " + set.size());//求元素的个数。
        System.out.println("set.remove(1) = " + set.remove(1));//移除:只能按照内容移除无法使用下标移除
        //set.clear();//清空
        System.out.println("set.isEmpty() = " + set.isEmpty());//是否为空
        System.out.println("set.contains(1) = " + set.contains(1));//判断是否在集合中
        Iterator iterator = set.iterator();
        while (iterator.hasNext()){//遍历只能使用foreach或迭代
            Object o = iterator.next();
            System.out.println(o);
        }
    }
}

TreeSet中添加自定义类时实现自然排序接口
自定义类

package lx01;

public class Animals implements Comparable<Animals>{
    private String name;
    private int age;
    private String Species;

    public Animals() {
    }

    public Animals(String name, int age, String species) {
        this.name = name;
        this.age = age;
        Species = species;
    }

    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;
    }

    public String getSpecies() {
        return Species;
    }

    public void setSpecies(String species) {
        Species = species;
    }

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

    @Override
    public int compareTo(Animals o) {
        if (!(this.age==o.age)){
            return this.age-o.age;
        }
        return 0;
    }
}

测试类

package lx01;

import org.junit.Test;

import java.util.TreeSet;

public class Demo01 {
    @Test
    public void test01(){
        //里面的元素---具备排序功能【因为TreeSet元素有序==要求里面的元素具有排序规则。1.自然排序comparable 2. 指定定制排序 】
        //自定义类里面没有排序规则,而TreeSet需要根据排序规则排序,所以我们要实现Comparable<Animals>接口,重写里面的compareTo()方法定义规则
        TreeSet treeSet=new TreeSet();
        treeSet.add(new Animals("团子",2,"猫"));
        treeSet.add(new Animals("旺财",3,"狗"));
        System.out.println(treeSet);
    }
}

定制排序
如果后期往TreeSet中添加的元素类型为系统定义的类对象,而系统定义的类没有实现Comparable接口,如何往TreeSet中添加该类型。 这时就需要定制排序。
实体类

package lx01;

public class Animals{
    private String name;
    private int age;
    private String Species;

    public Animals() {
    }

    public Animals(String name, int age, String species) {
        this.name = name;
        this.age = age;
        Species = species;
    }

    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;
    }

    public String getSpecies() {
        return Species;
    }

    public void setSpecies(String species) {
        Species = species;
    }

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


}

创建定制排序类

package lx01;

import java.util.Comparator;

public class MyCompare implements Comparator<Animals> {

    @Override
    public int compare(Animals o1, Animals o2) {
        return o1.getAge()- o2.getAge();
    }
}

测试类

package lx01;

import org.junit.Test;

import java.util.TreeSet;

public class Demo01 {
    @Test
    public void test01(){
         //Comparator comparator=new MyCompare()
        //Comparator<? super E> comparator: 它是一个定制排序接口。 多态
        TreeSet treeSet=new TreeSet(new MyCompare());//TreeSet中的元素会按照指定的定制排序排序了。
        treeSet.add(new Animals("团子",2,"猫"));
        treeSet.add(new Animals("旺财",3,"狗"));
        System.out.println(treeSet);
    }
}

HashMap
public class HashMap<K,V>
extends AbstractMap<K,V>
implements Map<K,V>, Cloneable, Serializable基于哈希表的实现的Map接口。它是一种key-value映射集合。
此实现提供了所有可选的Map操作,并允许null的值和null键。 ( HashMap类大致相当于Hashtable ,除了它是不同步的,并允许null)。这个类不能保证Map的顺序; 特别是,它不能保证排序在一段时间内保持不变。
使用HashMap===必须会

 @Test
    public  void tese02(){
        //创建了一个HashMap对象: 特点:1.key不允许重复 但是value允许重复 2. key和value都可以为null.
        HashMap map=new HashMap();
        //存放元素 put(key,value).
        map.put("name","小木");
        map.put("age",18);
        map.put("sex","男");
        map.put("major","java");
        map.put("school","北大");
        map.put("name","小木不痞");//出现重复key时,后者把前置覆盖value值。
        System.out.println(map);
        map.remove("school");//移除---根据key移除。
        System.out.println(map);
        Object age= map.get("age");//查找--根据key查询
        System.out.println(age);
        //1.获取map中所有的key.
        Set keys = map.keySet();
        System.out.println(keys);
        //2. 获取map中所有的值
        Collection values = map.values();
        System.out.println(values);
        //3.遍历map.
        //遍历map中所有的key
        for(Object k:keys){
            Object o = map.get(k);//根据key获取对应的值
            System.out.println(k+"=============>"+o);
        }
        map.clear();
        map.size();
        map.isEmpty();
        map.containsKey("");//判断map中是否包含指定的key
    }

底层源码
底层按照数组+链表 数组+红黑树实现。

集合的架构图。


泛型.
什么是泛型
限制集合中元素的类型。 集合中 <K,V> 他们都是泛型的标志符。如果我们在创建集合时为里面的泛型标志指定了类型,则集合中只能添加该类型的元素。
集合的工具类.Collections
概述
该类所有的方法都是static静态方法,提供的这些静态方法都是用来操作集合对象。
常用的方法

    @Test
    public void test01(){
       //我们在创建List接口对象时,指定了<>中类型。表示这个集合对象中只能添加字符串类型的元素.
        // 当遍历时拿到的类型一定时泛型指定的类型,无需进行强制转换。避免数据安全问题。
        List<String> list=new ArrayList();//创建对象,规定泛型
        list.add("小木");//规定过泛型之后,不能使用别的类型;
        list.add("不痞");
        list.add("19");
        System.out.println(list);
        Collections. reverse(list);//翻转
        System.out.println(list);
        Collections.sort(list);//排序
        System.out.println(list);
        Collections.shuffle(list);//打乱
        System.out.println(list);
    }

posted on 2024-08-31 14:42  小木不痞  阅读(19)  评论(0编辑  收藏  举报

导航