Java学习

阅读: 评论:0

Java学习

Java学习

一、集合框架

1.1 目录

1. 为什么使用集合?

2. 集合架构有哪些?

3. List集合

4. ArrayList集合

5. LinkedList集合。

6. Set集合

7. HashSet集合

8. TreeSet集合。

9. Map

10.HashMap集合。

11.TreeMap集合。

1.2 为什么使用集合?

1. 数组.

  思考: 数组有缺陷?--定容【定数组定义好,他们的长度就无法改变.】如果需要改变数组的长度,代码就变得很复杂。

2.我们是否可以定义一个长度可改变的容器。---当然可以。

3.手写可变长度的容器。

  //自定义可变长度的容器类。

public class MyArray {

     private Object [] arr; //声明一个Object类型的数组

     private int size;//表示数组的下标 因为他们是类成员变量 在创建对象时都有默认值。

     public MyArray(){ //无参构造函数

            this(3); //本类中其他的构造函数 this本类的对象。 如果在构造方法中this()表示调用本类的其他构造函数

     }

     public MyArray(int initSize){ //有参构造函数--表示数组的长度

          if(initSize<0){ //长度不合法

              throw new RuntimeException("sorry 数组的长度有误。");

          }

          arr=new Object[initSize];

     }

     //把元素o放入数组arr

     public void addData(Object o){

           //判断你的数组是否已满

           if(size>=arr.length){

                //扩容--(1)容器的长度变长 (2)把原来容器中的元素复制到新的容器中

               Object[] newArr = pyOf(arr, size * 2);

               arr=newArr;

           }

           arr[size]=o;

           size++;

     }

     //根据下标获取数组中的元素。

     public Object getData(int index){

            if(index>=size){

                 throw new ArrayIndexOutOfBoundsException("下标越界");

            }

           Object o = arr[index];

            return o;

     }

}

我们自己可以手写一个可变的容器,那么别人也可以手写可变的容器。

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

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

1.3 集合的架构

1.4 List集合-ArrayList

1.4.0 创建集合对象

   List list = new ArrayList(); //创建一个集合对象 如果没有指定集合容器的长度默认为10

   List list1 = new ArrayList(15);

1.4.1 添加的操作

        //添加 (1)可以添加任意类型

        list.add("java01");

        list.add("java02");

        list.add(15.5);

        list.add(18);

        list.add(true);

        list.add(new Date());

        System.out.println(list);

        list.add(2,"hello"); //下标为2的位置添加元素 并把后面的元素进行唯一

        System.out.println(list); //打印一个对象时默认调用的为toString()

        List list2=new ArrayList();

        list2.add("a");

        list2.add("b");

        list.addAll(list2);//添加多个元素 把list2中的每个元素一一添加到list中

        System.out.println(list);

1.4.2 删除的操作

   //删除操作

        ve(2);//移除下标为2的元素

        System.out.println(list);

        list.clear();//清空集合中的元素.

        System.out.println(list);

1.4.3 修改的操作

        //修改操作

        list.set(1,"刘德华");

        System.out.println(list);

1.4.4 查询操作

List list = new ArrayList();

        list.add("java01");

        list.add("java02");

        list.add("java03");

        list.add("java02");

        //查询的方法

        Object o = (1);//根据下标获取元素

        System.out.println(o);

        int size = list.size();//获取集合中元素的个数。

        System.out.println(size);

        boolean f = ains("java05");//判断元素是否在集合中

        System.out.println(f);

        int index = list.indexOf("java05");//查询元素在集合中第一次出现的位置

        System.out.println(index);

        //遍历集合中的元素 for循环

        for(int i=0;i<list.size();i++){

            Object o1 = (i);

            System.out.println(o1);

        }

1.4.5 ArrayList底层源码

java

从构造方法来入手。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的底层就是基于数组实现的。

1.5 LinkedList

它是一个链表结构。

1.5.1 添加

  //添加

        linkedList.add("java01"); //追加尾部

        linkedList.addFirst("java02"); //添加到头部

        linkedList.addLast("java03");//追加到尾部

        linkedList.addFirst("java04"); //追加到头部

        linkedList.addLast("java05");//追加到尾部

        System.out.println(linkedList);

1.5.2 删除操作

    //删除操作

        veFirst();//移除头部元素

        System.out.println(linkedList);

        ve(2);//移除指定位置的元素

        System.out.println(linkedList);

        veLast();//移除尾部的元素

        System.out.println(linkedList);

1.5.3 修改操作

   //修改操作

        linkedList.set(1,"java11");

        System.out.println(linkedList);

 1.5.4 查询操作

        //查询操作

        int size = linkedList.size();//求长度

        boolean empty = linkedList.isEmpty();//是否为空

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

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

        Object first = First();//获取第一个元素

        System.out.println(first);

        Object last = Last();

        System.out.println(last);

1.回顾

1. 集合: 理解为容器--它的长度可以变大的,而且它存储的都是对象类型。
2. List:----->它是一个接口,该集合中的元素可以重复,而且是有序的(有下标)。
     ArrayList--->它的底层是数组结构,它的查询效率高,但是它的添加和删除效率低--因为它要牵涉到数据的迁移。
     常见方法: add();size();indexOf(); contains(); get();remove();isEmpty();
    
     LinkedList--->底层双向链表结构,它增加和删除效率高,但是它的查询效率低。
      常见的方法: 它的方法和ArrayList中的方法比较相似,它也有自己特有的方法:
      addFirst() addLast() getFirst()  getLast()

2. 正文

1. LinkedList的底层源码。
2. Set集合 
3. HashSet
4. TreeSet
5. Map集合
6. HashMap
6. TreeMap

3.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;
            = 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查询效率低。因为它要一个节点一个节点的往后找。

 4.Set集合

4.1 HashSet集合

4.1.1 创建HashSet对象。

public class Test02 {public static void main(String[] args) {HashSet  hashSet= new HashSet();
​HashSet  hashSet1 = new HashSet(16);//初始容器的大小
​//loadFactor:--->0.7f 表示负载因子 当空间使用70%时 要求扩容HashSet hashSet2 = new HashSet(16,0.7f);}
}

4.1.2 添加元素

     //添加操作hashSet.add("java01");hashSet.add("java02");hashSet.add("java04");hashSet.add("java03");hashSet.add("java02");
​HashSet set2=new HashSet();set2.add("刘德华");set2.add("张学友");set2.add("黎明");hashSet.addAll(set2); //把set2中的每个元素添加到hashset中System.out.println(hashSet); //元素不能重复 而且无序

4.1.3 删除

        //删除ve("黎明");
//        hashSet.clear();//清空容器集合System.out.println(hashSet);

4.1.4 修改

        //修改操作boolean empty = hashSet.isEmpty(); //判断是否为空System.out.println(empty);
​boolean b = ains("刘德华");//判断元素是否在容器中System.out.println(b);

4.1.5 hashSet的遍历

(1)通过foreach遍历

 //遍历--- foreachfor(Object o: hashSet){System.out.println(o);}

(2)通过迭代器来遍历

 //迭代器遍历Iterator iterator = hashSet.iterator();//获取迭代器对象 有序:有下标while (iterator.hasNext()){//判断是否指定能够移动Object next = ();//指定移动并获取当前的元素System.out.println(next);}

4.1.6 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。我们说hashset的底层原理时,我们就在后HashMap的原理就行。 讲HashMap时给大家说原理。

4.2 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 Test04 {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

解决办法有两个:

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

package com.ykq;
​
import java.util.TreeSet;
​public class Test04 {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;
​@Overridepublic 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表示相同元素。@Overridepublic 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=new TreeSet(); 但是在创建对象时 并没有为其指定排序得规则,那么就要求该集合得元素有排序规则。 如果元素得类已经创建完成,不能修改该类得源码,这时我们又想把该类得对象放入得TreeSet容器中。 这时就需要你在创建TreeSet时指定排序得规则。
public class MyComparator implements Comparator {
​//需要比对得两个对象@Overridepublic int compare(Object o1, Object o2) {Student s1= (Student) o1;Student s2= (Student) o2;Age()&Age()){return 1;}else Age()&Age()){return -1;}else {return 0;}}
}public class Demo01 {public static void main(String[] args) {//Comparator<? super E> comparator//为TreeSet容器指定了排序规则TreeSet treeSet=new TreeSet(new MyComparator()); treeSet.add(new Student(18,"王亚波"));treeSet.add(new Student(17,"潘世浩"));treeSet.add(new Student(19,"刘晓东"));treeSet.add(new Student(19,"娄明阳"));System.out.println(treeSet);}
}  

1. 回顾

1. 上节课-----集合
2. list--->LinkedList: 底层基于双向链表,增加和删除效率比较高(只需要改变链表得指向),它得查询效率慢(每次查询数据 从头查询或者从尾查询)。3. Set----> 无序,不可重复。HashSet---->底层依据于HashMap。TreeSet---->基于二叉树,它是一个有序得集合,要求里面得元素必须实现Comparable接口。 按照该接口得方法进行排序以及去重。      

2. 正文

1. Map 属于key---value键值对元素。HashMap
2. 泛型
​
3. IOFile对象。

3. Map 属于键值对模式

map中得每个元素属于键值对模式。 如果往map中添加元素时 需要添加key 和 value. 它也属于一个接口,该接口常见得实现类有: HashMap.

3.1 如何创建Map对象

        //默认初始化大小为16 负载因子为0.75Map map=new HashMap();//初始化大小Map map2=new HashMap(16);//初始化大小  负载因子Map map3=new HashMap(16,0.78f);

 

3.2 添加操作

 //默认初始化大小为16 负载因子为0.75Map map=new HashMap();//添加操作 key: name     value: 高景霞map.put("name","高景霞"); //注意: 要求map得key必须唯一。map.put("age",18);map.put("name","李赛"); //因为key不能重复,所以后者会把前者覆盖
​Map m1=new HashMap();m1.put("k1","v1");m1.put("k2","v2");map.putAll(m1); //把m1中得每个元素 添加到map中
​map.putIfAbsent("age",28) ;//如果指定得key存在,则不放入map中,如果不存在则放入map中
​System.out.println(map);

3.3 删除操作

        //删除操作ve("age2");//根据指定得key移除元素System.out.println(map);map.clear(); //清空map容器System.out.println(map);

3.4 修改操作

     //修改操作place("name","刘德华");//替换元素System.out.println(map);

3.5 查询

public static void main(String[] args) {Map map=new HashMap();map.put("k1","v1");map.put("k4","v4");map.put("k2","v2");map.put("k3","v3");//查询操作boolean f = ainsKey("k5");//判断map是否存在指定得key
​Object v = ("k5"); //根据指定的key获取对应得value值System.out.println(v);
​Set keys = map.keySet();//返回该map中所有得keySystem.out.println(keys);//遍历map.for(Object k:keys){Object value= (k);//System.out.println(k+"================>"+value);}
​}

3.6 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个,则把链表转换为红黑二叉树。
​
​
JDK1.7和JDK1.8 HashMap得区别。JDK1.7使用得数据结构: 数组+链表  而且链表插入模式为头部插入(造成死循环)。jdk1.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;
    }

本文发布于:2024-02-02 01:55:29,感谢您对本站的认可!

本文链接:https://www.4u4v.net/it/170681387640662.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

标签:Java
留言与评论(共有 0 条评论)
   
验证码:

Copyright ©2019-2022 Comsenz Inc.Powered by ©

网站地图1 网站地图2 网站地图3 网站地图4 网站地图5 网站地图6 网站地图7 网站地图8 网站地图9 网站地图10 网站地图11 网站地图12 网站地图13 网站地图14 网站地图15 网站地图16 网站地图17 网站地图18 网站地图19 网站地图20 网站地图21 网站地图22/a> 网站地图23