Java 集合框架

目录

为什么要用集合

集合架构

 List集合

 ArrayList

创建集合对象

集合添加操作

集合删除操作

集合修改操作

集合查询操作

 ArrayList底层源码

LinkedList

LinkedList添加操作

LinkedList删除操作

LinkedList修改操作

LinkedList查询操作

LinkedList的底层源码。

 分析: LinkedList查询效率低。因为它要一个节点一个节点的往后找。

 Set集合​

HashSet集合

创建HashSet集合对象

HashSet添加元素

HashSet删除操作

HashSet查询操作

迭代 

HashSet的源码



为什么要用集合

1.相对于数组,数组有缺陷》》》定容【数组一旦定义好,他们的长度就无法改变】,如果改变数组的长度就会很复杂。

2.可以定义一个长度可以改变的容器

手写可变长度的容器

public class MyArray {
    public Object [] arr;
    public int size;//数组下标

    public MyArray(){

    }
    public MyArray(int intSize){
        if (intSize<0){
            throw new ArrayIndexOutOfBoundsException("长度不合法");
        }
        arr = new Object[intSize];
    }

    public void addDate(Object a){
        //扩容
        if (size>= arr.length){
            Object[] newArr = Arrays.copyOf(arr, size * 2);
            arr=newArr;
        }
        //添加数组元素
        arr[size]=a;
        size++;
    }
    public Object getDate(int index){
        if (index<0){
            throw new ArrayIndexOutOfBoundsException("下标越界");
        }
        Object a = arr[index];
        return a;
    }
}

===========================================================
public class TestArray {
    public static void main(String[] args) {
        MyArray my = new MyArray(5);
        my.addDate("java01");
        my.addDate("java02");
        my.addDate("java03");
        Object date = my.getDate(0);
        System.out.println(date);
        for (int i = 0; i <my.size; i++) {
            Object o = my.getDate(i);
            System.out.println(o);
        }
    }
}

java官网 基于数组 根据不同的数据结构 创建多个类 而这些类统称为 集合框架

以后我们在说集合框架时,就表示多个类

集合架构

 List集合

 ArrayList

创建集合对象

//创建集合对象
        List list = new ArrayList();//默认集合长度为10
        //初始化集合空间  长度为 2
        List list1 = new ArrayList(2);

集合添加操作

//增加操作 集合可增加任意类型
        list.add(100);
        list.add(1.52525);
        list.add("张三");
        list.add(true);
        list.add(new Date());

        //在下标为 2 的位置添加元素,并把后面元素进行移位
        list.add(2,"李四");
        System.out.println(list);

        //添加多个元素 将list1的元素添加到list中
        list1.add("a");
        list1.add("b");
        list.addAll(list1);
        System.out.println(list);

集合删除操作

  //删除操作
        list.remove(2);
        System.out.println(list);
        //清空集合元素
        list.clear();
        System.out.println(list);

集合修改操作

 //修改操作
        list.set(1,"赵六");
        System.out.println(list);

集合查询操作

 //查询操作
        Object o = list.get(2);//根据下标获取元素
        System.out.println(o);
        //获取元素中的个数
        int size = list.size();
        System.out.println(size);
        //判断元素是否在集合中
        boolean zs = list.contains("张三");
        System.out.println(zs);//返回true/false
        //查询元素在集合中第一次出现的位置
        int ls = list.indexOf("李四");
        System.out.println(ls);//返回元素下标 没有返回-1

        //遍历集合中的元素 for循环方式
        for (int i = 0; i < list.size(); i++) {
            Object o1 = list.get(i);
            System.out.println(o1);
        }
        System.out.println("================");
        //遍历集合中的元素 foreach循环方式
        for (Object o2 : list
             ) {
            System.out.println(o2);
        }

 ArrayList底层源码

从构造方法来入手。new ArrayList(22) 底层声明了一个Object类型的数组 名字elementData
  Object[] elementData

  public ArrayList(int initialCapacity) {
        if (initialCapacity > 0) { //大于0
            this.elementData = new Object[initialCapacity];
        } else if (initialCapacity == 0) { //等于初始化为一个空数组
            this.elementData = EMPTY_ELEMENTDATA;
        } else { //抛出一个异常
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        }
    }

==========add("java01")======E理解为Object类型================  
   public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // 扩容 
        elementData[size++] = e;  //把元素赋值给数组的相应位置
        return true;
    }
==========indexOf("java02") 判断元素在集合中第一次的位置=============
     public int indexOf(Object o) {
        if (o == null) {
            for (int i = 0; i < size; i++)
                if (elementData[i]==null)
                    return i;
        } else {
            for (int i = 0; i < size; i++)
                if (o.equals(elementData[i])) //和数组中的每个元素内容进行比对
                    return i;  //返回元素在集合中位置
        }
        return -1;
    }   

===========size() 请求数组的长度======================
 public int size() {
        return size;
    }   

============contain("java05")判断元素是否在集合中==============
    public boolean contains(Object o) {
        return indexOf(o) >= 0;
    }
===============get(1) 获取指定位置的元素========
   public E get(int index) {
        rangeCheck(index); //判断指定的位置是否合法 

        return elementData(index);
    }  

    E elementData(int index) {
        return (E) elementData[index];
    } 

============toString() 为什么不打印对象的引用地址 
    [java01, java02, java03, java02]因为重写了Object里面的toString方法。
    
 public String toString() {
        Iterator<E> it = iterator();
        if (! it.hasNext())
            return "[]";

        StringBuilder sb = new StringBuilder();
        sb.append('[');
        for (;;) {
            E e = it.next();
            sb.append(e == this ? "(this Collection)" : e);
            if (! it.hasNext())
                return sb.append(']').toString();
            sb.append(',').append(' ');
        }
    }   
    
    
通过对ArrayList方法的底层代码分析:底层就是对数组的操作。
    ArrayList的底层就是基于数组实现的。

LinkedList

它是一个链表结构。

LinkedList添加操作

 //添加
        linkedList.add("java01"); //追加尾部
        linkedList.addFirst("java02"); //添加到头部
        linkedList.addLast("java03");//追加到尾部
        linkedList.addFirst("java04"); //追加到头部
        linkedList.addLast("java05");//追加到尾部
        System.out.println(linkedList);

LinkedList删除操作

  //删除操作
        linkedList.removeFirst();//移除头部元素
        System.out.println(linkedList);

        linkedList.remove(2);//移除指定位置的元素
        System.out.println(linkedList);

        linkedList.removeLast();//移除尾部的元素
        System.out.println(linkedList);

LinkedList修改操作

//修改操作
        linkedList.set(1,"java11");
        System.out.println(linkedList);

LinkedList查询操作

        //查询操作
        int size = linkedList.size();//求长度
        boolean empty = linkedList.isEmpty();//是否为空

        boolean b = linkedList.contains("java01");//判断元素是否在集合中

        Object o = linkedList.get(1);//根据下标获取指定位置的元素

        Object first = linkedList.getFirst();//获取第一个元素
        System.out.println(first);

        Object last = linkedList.getLast();
        System.out.println(last);

LinkedList的底层源码。

1.凡是查询源码 ,我们都是从类的构造方法入手:
     /**
     * Constructs an empty list.
     */
    public LinkedList() {
    }
 该类的构造方法内是空的,没有任何的代码。 但是该类中有三个属性。   
    transient int size = 0; //索引
   
    transient Node<E> first; //第一个元素对象 
   
    transient Node<E> last; //表示最后一个元素对象。
 
================ add的源码=====E:理解为Object类型==========================。
   public boolean add(E e) {
        linkLast(e);
        return true;
    }


   void linkLast(E e) {
        final Node<E> l = last;
        //上一个节点   数据  下一个节点
        final Node<E> newNode = new Node<>(l, e, null);
        last = newNode;
        if (l == null)
            first = newNode;
        else
            l.next = newNode;
        size++;
        modCount++;
    }
    
==================Node的源码 内部类=======================================   
   private static class Node<E> { //<E>泛型--object
        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;
        }
    }
1、==================== get(1)-----获取元素========================
   public E get(int index) {
        checkElementIndex(index); //检查index下标是否正确。
        return node(index).item;  //李四Node对象
    }
 ========================node(index)=============================
 Node<E> node(int index) {
        //>> 位运算二进制运算 ----- size >> 1 一半的意思size/2
        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;
        }
    }

 分析: LinkedList查询效率低。因为它要一个节点一个节点的往后找。

 Set集合

HashSet集合

创建HashSet集合对象

 //创建HashSet对象 默认长度为16  0.7f 表示负载因子:负载因子默认0.75f
        HashSet hashSet1 = new HashSet(10,0.7f);
        HashSet hashSet2= new HashSet();
        HashSet hashSet3 = new HashSet(16);//初始容器的大小

HashSet添加元素

 //添加操作
        hashSet.add("java01");
        hashSet.add("java02");
        hashSet.add("java04");
        hashSet.add("java03");
        hashSet.add("java02");
        hashSet.add("刘德华");
        System.out.println(hashSet);//元素无序 不重复
        HashSet hashSet1 = new HashSet();
        hashSet1.add("刘德华");
        //判断是否能添加
        boolean b = hashSet.addAll(hashSet1);
        boolean b1 = hashSet1.addAll(hashSet);
        System.out.println(b);
        System.out.println(b1);
        //将hashSet元素添加到hashSet1中
        hashSet1.addAll(hashSet);
        System.out.println(hashSet1);

HashSet删除操作

//删除操作
        hashSet.remove("java04");
    //    hashSet.clear();//清空操作
        System.out.println(hashSet);

HashSet查询操作

HashSet没有修改操作

 //查询操作
        boolean empty = hashSet.isEmpty();//判断是否为空
        System.out.println(empty) ;
        boolean l = hashSet.contains("刘德华"); //判断元素是否在集合中 返回true/false
        System.out.println(l);

        //遍历---foreach/迭代器
//        for (Object o:hashSet
//             ) {
//            System.out.println(o);
//        }
        //迭代器
        Iterator iterator = hashSet.iterator();//获取迭代器对象
        while (iterator.hasNext()){//判断迭代器指针能够移动
            Object next = iterator.next();//指针移动并获取当前元素
            System.out.println(next);
        }

迭代 

HashSet的源码

从构造函数说起:
    /**
     * Constructs a new, empty set; the backing <tt>HashMap</tt> instance has
     * default initial capacity (16) and load factor (0.75).
     */
    public HashSet() {
        map = new HashMap<>();
    }
 //在创建一个HashSet的对象时,底层创建的是HashMap

TreeSet集合。

TreeSet中的方法和HashSet中的方法一模一样 只是他们的实现不一样。
TreeSet 基于TreeMap 实现。TreeSet可以实现有序集合,但是有序性需要通过比较器实现。

String 类型

        TreeSet treeSet=new TreeSet();
        treeSet.add("java05");
        treeSet.add("java03");
        treeSet.add("java04");
        treeSet.add("java01");
        treeSet.add("java02");
        treeSet.add("java04");

        System.out.println(treeSet);

存储一个对象类型:

public class Demo04 {
    public static void main(String[] args) {
        TreeSet treeSet=new TreeSet();
        treeSet.add(new Student("王俊凯",17));
        treeSet.add(new Student("赵晓普",16));
        treeSet.add(new Student("赵俊涛",16));
        treeSet.add(new Student("徐志豪",15));

        System.out.println(treeSet);
    }
}

通过运行我们发现出现如下的错误:

发现: TreeSet中的元素必须实现Comparable接口 方可放入TreeSet

String类型是已经用了Comparable接口

解决办法有两个:

第一个: 让你的类实现Comparable接口

public class Demo04 {
    public static void main(String[] args) {
        TreeSet treeSet=new TreeSet(); //TreeSet不允许重复元素
        treeSet.add(new Student("王俊凯",17));
        treeSet.add(new Student("赵晓普",16));
        treeSet.add(new Student("赵俊涛",16));
        treeSet.add(new Student("徐志豪",15));

        System.out.println(treeSet);
    }
}
class Student implements Comparable{
     private String name;
     private Integer age;

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

    public Student(String name, Integer age) {
        this.name = name;
        this.age = age;
    }

    public Student() {
    }

    public String getName() {
        return name;
    }

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

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }
   //排序:---返回如果大于0 表示当前元素比o大  如果返回-1 当前添加的元素比o小  返回0表示相同元素。
    @Override
    public int compareTo(Object o) {
        Student student= (Student) o;
        System.out.println(this+"===================>"+o);

        if(this.age>student.age){
            return 1;
        }
        if(this.age<student.age){
            return -1;
        }

        return 0;
    }
}

第二种: 在创建TreeSet时指定排序的对象。

创建TreeSet时 为其指定排序得规则

创建TreeSet对象。
  TreeSet treeSet=new TreeSet();

在创建对象时 并没有为其指定排序得规则,那么就要求该集合得元素有排序规则。 如果元素得类已经创建完成,不能修改该类得源码,这时我们又想把该类得对象放入得TreeSet容器中。 这时就需要你在创建TreeSet时指定排序得规则。

public class Demo01 {
    //避免修改程序 可以增加类
    public static void main(String[] args) {
        TreeSet treeSet = new TreeSet(new MyComparator());//为TreeSet提供指定排序规则
        treeSet.add(new Student("张三",12));
        treeSet.add(new Student("李四",12));
        treeSet.add(new Student("王五",13));
        treeSet.add(new Student("赵六",14));
        System.out.println(treeSet);
    }
}
class Student{
    private String name;
    private int age;

    public Student() {

    }

    public Student(String name, int age) {
        this.name = name;
        this.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;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
===================================================
public class MyComparator implements Comparator {
    @Override
    public int compare(Object o1, Object o2) {
        Student s1 = (Student) o1;
        Student s2 = (Student) o2;
        if (s1.getAge()>s2.getAge()){
            return 1;
        }else if (s1.getAge()<s2.getAge()){
            return -1;
        }else {
            return 0;
        }
    }
}

Map

属于键值对模式

map中得每个元素属于键值对模式。

如果往map中添加元素时 需要添加key 和 value. 属于一个接口

该接口常见得实现类有: HashMap.

创建Map对象

public class Demo02 {
    public static void main(String[] args) {
        //默认空间16 负载因子0.75f
        Map map = new HashMap();
        //初始大小
        Map map1 = new HashMap(18);
        // 初始大小  初始负载因子
        Map map2 = new HashMap(12,0.3f);
}

添加操作

  //添加
        map.put("k1","v1");
        map.put("k4","v2");
        map.put("k3","v3");
        map.put("k4","v4");  //key 不能重复 后者会覆盖前者 value值相同不影响

        map1.put("f1","v1");
        map.putAll(map1);

        map.putIfAbsent("k5","12");//如果key存在则不放入 key不存在则放入
        System.out.println(map);

删除操作

//删除操作
        map.remove("k4");//删除key
        System.out.println(map);
        //map.clear();//清空操作

修改操作

//修改操作
        map.replace("k1","123");
        System.out.println(map);

查询操作

//查询操作

        boolean k1 = map.containsKey("k1");//根据key查询是否存在
        System.out.println(k1);
        boolean b = map.containsValue("324");//根据value查询是否存在
        System.out.println(b);

        Object k11 = map.get("k1");//根据key获取对应的值
        System.out.println(k11);

        Set set = map.keySet();
        System.out.println(set);//获取所有的key

        //遍历
        for (Object k:set
             ) {
            Object o = map.get(k);
            System.out.println(k+" "+o);
        }

HashMap得底层原理

 JDK1.7 和 JDK1.8他们是有区别得。
   JDK1.7使用得数据结构: 数组+链表  而且链表插入模式为头部插入(造成死循环)。
   jdk1.8使用得数据结构: 数组+链表+红黑树 而且链表得插入模式为尾部插入。

从构造函数入口:
  /**
     * Constructs an empty <tt>HashMap</tt> with the default initial capacity
     * (16) and the default load factor (0.75).
     */
   public HashMap() {
        this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
    }

 JDK1.8 HashMap原理
Hashmap得原理,存储元素使用得put(key,value),根据key得hash计算出相应得哈希值,根据相应得算法求出该元素在数组中得位置, 如果求出得哈希值相同,则称为哈希冲突,会根据equals来判断元素是否一致,如果equals不同,则存入单向链表上, 如果哈希碰撞得个数超过8个,则把链表转换为红黑二叉树。

final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
                   boolean evict) {
        Node<K,V>[] tab; Node<K,V> p; int n, i;
        if ((tab = table) == null || (n = tab.length) == 0)
            n = (tab = resize()).length;
        if ((p = tab[i = (n - 1) & hash]) == null)
            tab[i] = newNode(hash, key, value, null);
        else {
            Node<K,V> e; K k;
            if (p.hash == hash &&
                ((k = p.key) == key || (key != null && key.equals(k))))
                 //如果key得hash值相同,判断key得equals是否相同,替换原来得元素
                e = p;
            else if (p instanceof TreeNode)
                e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
            else {
                for (int binCount = 0; ; ++binCount) {
                    if ((e = p.next) == null) {
                        p.next = newNode(hash, key, value, null);
                        // 判断链表得长度是否超过8个
                        if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
                            // 把链表转换为红黑树结构
                            treeifyBin(tab, hash);
                        break;
                    }
                    if (e.hash == hash &&
                        ((k = e.key) == key || (key != null && key.equals(k))))
                        break;
                    p = e;
                }
            }
            if (e != null) { // existing mapping for key
                V oldValue = e.value;
                if (!onlyIfAbsent || oldValue == null)
                    e.value = value;
                afterNodeAccess(e);
                return oldValue;
            }
        }
        ++modCount;
        if (++size > threshold)
            resize();
        afterNodeInsertion(evict);
        return null;
    }

 

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值