JavaSE学习总结---集合

集合概述

什么是集合?有什么用?
数组其实就是一个集合,集合是一个容器,是一个载体可以容纳多个对象。
在实际开发中,假设连接数据库,数据库当中有十条记录,把这十条记录查出来,java将这十条数据封装成十个对象放到集合传到前端,遍历集合,将数据展示出来

集合不能直接存储基本数据类型,另外集合也不能直接存储java对象
集合当中存储的都是java对象的内存地址。(或者说集合中存储的是引用。)
list.add(100); //自动装箱Integer
注意
集合在java中本身是一个容器,是一个对象。
集合中任何时候存储的都是“引用”。

在java中每一个不同的集合,底层会对应不同的数据结构
往不同的集合中
存储元素,等于将数据放到了不同的数据结构当中。什么是数据结构?数据存储的
结构就是数据结构。不同的数据结构,数据存储方式不同。例如:
数组、二叉树、链表、哈希表…
以上这些都是常见的数据结构。

	你往集合c1中放数据,可能是放到数组上了。
	你往集合c2中放数据,可能是放到二叉树上了。
	.....
	你使用不同的集合等同于使用了不同的数据结构。

java中已经将数据结构实现了,已经写好了这些常用的集合类,只需要掌握怎么用?在什么情况下选择哪一种合适的集合去使用即可。

    new ArrayList(); 创建一个集合,底层是数组。
	new LinkedList(); 创建一个集合对象,底层是链表。
	new TreeSet(); 创建一个集合对象,底层是二叉树。

集合类和相关的接口都在java.util包下

Java 集合可分为 Set、List 和 Map 三种大体系

有个继承图在word文档中

Set:无序、不可重复的集合,没有下标
List:有序,可重复的集合,有序的意思是存进去的顺序,取出来也是这个顺序,表现为都有下标
list接口和set接口都继承了collection接口,这个接口又继承了Iterable接口(可遍历的)
Map:具有映射关系的集合,键值对方式存储,跟collection接口没有关系

Collection接口中常用方法

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
/*  Collection接口中常用方法,这是超级父接口,孩子都能用
    1,add,向集合中添加元素
    2,size,集合中元素的个数
    3,clear 清空集合
    4,contains(object o),判断集合是否包含元素o
    5,remove,删除元素
    6,isEmpty,判断集合是否为空
    7,toArray,将集合转化为数组,用的不多
    8,迭代器,适合所有Collection以及子类中使用,Map不能用

 */

public class Test1 {
    public static void main(String[] args) {
        Collection c =new ArrayList();//多态,因为Collection是一个接口,不能实例化
        //接口Collection中的常用方法
        c.add(12);//自动装箱,实际上放了一个对象的内存地址,Integer x=new Integer(12)
        c.add(new Object());
        System.out.println("集合中的元素个数为: "+c.size());

        c.clear();
        System.out.println("集合中的元素个数为: "+c.size());

        c.add("王者");
        c.add("星耀");
        c.add("钻石");
        c.add("铂金");
        c.add("黄金");
        c.add(new Object());
        c.add(true);

        // Object [] objects=c.toArray();

        System.out.println(c.contains("青铜"));

        System.out.println(c.contains("黄金"));
        c.remove("黄金");
        System.out.println(c.contains("黄金"));

        System.out.println(c.isEmpty());


        //对集合Collection进行遍历/迭代
        //第一步,获取集合对象的迭代器对象Iterator it,相当于一个箭头,最初指向的是-1位置
        Iterator it =c.iterator();

        //第二步,通过以上获取的迭代器对象开始迭代/遍历集合
        /*
        🔺集合结构发生改变,迭代器要重新获取,也就是说,如果往集合中加入元素删除元素之后要重新获得迭代器
        迭代器对象Iterator it的方法:
            1,Boolean hasNext(),返回true则说明仍然有元素可以迭代
            2,Object next(),让迭代器前进一位,并且将指向的元素拿到
         */
//        if (it.hasNext()){
//            //不管当初存进去的是什么,取出来的统一是Object,这是取一次
//            Object o =it.next();
//            System.out.println(o);
//        }
        while(it.hasNext()){
            Object o =it.next();
            System.out.println(o);
        }



    }
}

详解contains方法

import java.util.ArrayList;
import java.util.Collection;

public class Test01 {
    public static void main(String[] args) {
        Collection c =new ArrayList();
        String s1=new String("abc");
        c.add(s1);
        String s2=new String("dfg");
        c.add(s2);

        String x =new String("abc");

        boolean flag=c.contains(x);
        System.out.println(flag);//输出是true,因为contains方法调用了String类的equals方法,比较的是内容(调用的是对象的equals方法)
        //如果调用的是没有重写过的equals方法,比较的就是内存地址,放在集合里的对象要重写equals方法
//remove也是调用equals方法,所以删了x,s1也没了
//自己写的类如果也加入到集合中,就要重写equals方法

    }
}

迭代器删除方法

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;

public class Test1 {
    public static void main(String[] args) {
        Collection c =new ArrayList();//多态,因为Collection是一个接口,不能实例化


        c.add("王者");
        c.add("星耀");
        c.add("钻石");
        c.add("铂金");
        c.add("黄金");
        c.add(new Object());
        c.add(true);


        Iterator it =c.iterator();
        while(it.hasNext()){
            Object o =it.next();
           // c.remove(o);//这里删除,集合结构变了,没有重新获得迭代器所以异常
            it.remove();//迭代器删了快照里面的
            System.out.println(o);//因为赋值给了o,输出的是o的值
        }
        System.out.println(c.size());



    }
}

Collection接口子接口list接口下独有的方法

list集合存储元素,有序可重复,有下标
list有特色的子方法

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

/*
   list特有的方法:
    1,E get(int index)
    2,void add(int index, E element)
    3,int indexOf(Object o)
    4,int lastIndexOf(Object o)
    5,E remove(int index)
    6,E set(int index, E element)
 */
public class ListTest01 {
    public static void main(String[] args) {
        List list=new ArrayList();

        list.add("1");
        list.add("3");
        list.add(new Object());

        list.add(1,"king");

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

        System.out.println(list.get(2));
        //因为有下标,所以list集合有自己特殊的遍历方法
        for (int i = 0; i <list.size() ; i++) {
            Object o=list.get(i);
            System.out.println(o);
        }
        //获取第一次出现的指引
        System.out.println(list.indexOf("king"));
        //获取最后一次出现的指引
        System.out.println(list.lastIndexOf("king"));

    }
}

ArrayList

底层是数组

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;

//ArrayList底层是一个数组,尽可能少的扩容
//Arraylist集合用的最多,底层是一个数组,向数组末尾添加元素效率不受影响,检索/查找某个元素操作比较多
public class ArrayListTest {
    public static void main(String[] args) {
        //底层先创建了一个长度为0的数组,当添加第一个元素的时候,初始化容量为0
        ArrayList a=new ArrayList();//初始化容量是10,size方法是获取元素的个数,不是容量
        System.out.println(a.size());
        
        ArrayList a1=new ArrayList(20);//初始化一个容量为20的
        //如果容量满了,自动扩容到原容量的1.5倍,右移一位,>>1,二进制右移一位

        Collection c=new HashSet();
        c.add(122);
        c.add(222);
        //通过这种构造方法将hashset集合转换成了list集合
        ArrayList a2=new ArrayList(c);
        
        
    }
}
import java.util.ArrayList;
import java.util.Collections;

public class ArrayListTest {
    public static void main(String[] args) {
        
        ArrayList a=new ArrayList();//非线程安全的
        //变成线程安全的
        Collections.synchronizedList(a);//工具类Collections的方法
        
        a.add("111");
        a.add("222");
      
    }
}

Linkedlist
Linkedlist底层是一个双向链表,也有下标,ArrayList检索效率高不是因为有下标,而是因为底层是数组
单向链表
优点:随机增删元素的效率高,因为增删元素不涉及大量元素的位移
缺点:查询效率较低,得从头开始在这里插入图片描述

/*
    单链表中节点是基本单元
    每个节点Node有两个属性
    1,存储的数据
    2,下一个节点的内存地址
 */
public class Node {
    Object o; //存储的数据
    Node next;//下个节点的内存地址

    public Node() {
    }

    public Node(Object o, Node next) {
        this.o = o;
        this.next = next;
    }
}

public class Link {
    //头节点
    Node header=null;

    //向末尾加元素
    public void  add(Object o){
        //创建一个新节点对象
        //让之前单链表的末尾节点next指向新节点对象
        //有可能元素是第一个
        if(header==null){
            //说明还没有节点,new个新的作为头节点,这个时候既是头又是末尾节点
           header= new Node(o,null);
        }else{
            //说明头不是空,得找出末尾节点,让next指向,新增的节点
            Node currentLastNode=findLast(header);
            currentLastNode.next=new Node(o,null);

        }

    }

    private Node findLast(Node node) {
        if(node.next==null){
            //如果一个节点的next是null
            //说明这个节点是末尾节点,返回这个节点就行了
            return  node;
        }
        //程序到这了就说明,node不是末尾节点
        //node.next是一个节点,存的是下个节点的地址,这是递归
        return findLast(node.next);
    }

    public  void del(Object o){

    }
    public void update(Object no){

    }
    public  void get(Object o){

    }
}

双向链表在这里插入图片描述
linkedlist集合没有初始化容量,最初链表没有任何元素,first和last都是null,开发不需要关心具体什么集合,面向接口编程,直接调用接口的方法,只不过底层对象不一样

Vector
底层也是数组,默认容量10,扩容后是原容量的2倍,线程安全,很少用

泛型

使用泛型后,迭代器返回的是泛型类型的数据不再是object了

import java.util.ArrayList;
import java.util.Iterator;
/*
    泛型是编译阶段特性
    优点:1,集合中存储的元素类型统一
         2,从集合中迭代中的数据类型统一,不需要大量的强转了,调用子类特有的方法还要转
    缺点:集合中的元素缺少多样性
 */
public class Fanxing {
    public static void main(String[] args) {
        Cat c=new Cat();
        Dog d=new Dog();


        ArrayList<Animal> list =new ArrayList<Animal>();
        list.add(c);
        list.add(d);

        Iterator<Animal>it=list.iterator(); //迭代器泛型使用
        while (it.hasNext()){
            Animal a=it.next();
            a.move();
        }
    }
}

class  Animal{
    public void move(){
        System.out.println("动物在移动");
    }
}
class Cat extends Animal{
    public  void cat(){
        System.out.println("抓老鼠");
    }
}
class  Dog extends  Animal{
    public  void dog(){
        System.out.println("吃骨头");
    }
}

jdk8之后引入自动类型推断

ArrayList<Animal> list =new ArrayList<>();//后面尖括号不用写

foreach

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

public class Foreach {
    public static void main(String[] args) {
        int [] a={1,2,3,4,5};
        //遍历
        for (int i = 0; i <a.length ; i++) {
            System.out.println(a[i]);
        }
        //foreach增强循环,缺点没有下标
        /*for(元素类型 变量名:数组或集合){
            System.out.println(变量名);
        }
        */
        for (int data:a) {
            System.out.println(data);
        }

        List<String> list =new ArrayList<>();
        list.add("222");
        list.add("sad");
        list.add("hello");
        //遍历使用迭代器
        Iterator<String> it =list.iterator();
        while (it.hasNext()){
            String s=it.next();
            System.out.println(s);
        }

        //使用下标,只针对有下标的
        for (int i = 0; i <list.size() ; i++) {
            String s1=list.get(i);
            System.out.println(s1);
        }

        //使用foreach
        for (String s3:list  //因为泛型是String
             ) {
            System.out.println(s3);
        }
    }
}

接口Map的常用方法

与上面的Collection没有继承关系

import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

/*
java.util.Map接口常用的方法:
    V put(K key, V value)  向Map集合中添加键值对
    void clear()  清空集合
    boolean containsKey(Object key)  判断是否包含某个key
    boolean containsValue(Object value)  判断是否包含某个value
    V get(Object key) 通过key获取value
    boolean isEmpty() 判断集合元素个数是否为0
    int size() 获取键值对的个数
    V remove(Object key) 通过key删除键值对

    Set<K> keySet() 获取Map所有的key,返回的是set集合
    Collection<V> values() 获取所有的value,返回一个Collection集合

    Set<Map.Entry<K,V>> entrySet() 将Map集合转换成set集合
    set集合对象就是1=zhangsan,2=lisi,....,set集合中元素的类型是Map.Entry,跟String都一样是类型的名字,Map.Entry是静态内部类

 */
public class MapTest {
    public static void main(String[] args) {
        //创建Map集合
        Map<Integer,String> map=new HashMap<>();
        map.put(1,"张三1");
        map.put(2,"张三2");
        map.put(3,"张三3");

        String s1 =map.get(2);
        System.out.println(s1);

        System.out.println("键值对的数量是: "+map.size());

        map.remove(2);
    //contains方法底层调用equals方法,自定义的类需要要重写equals方法
        System.out.println(map.containsKey(1));
        System.out.println(map.containsValue("李四"));
    
        
        Collection<String> c =map.values();
        for (String s:
             c) {
            System.out.println(s);
        }
    }
}

遍历Map集合

/*
遍历Map集合
 */
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

public class MapTest01 {
    public static void main(String[] args) {
        Map<Integer,String>map=new HashMap<>();
        map.put(1,"我");
        map.put(2,"是");
        map.put(3,"中");
        map.put(4,"国");
        map.put(5,"人");
        //第一种方式,通过获取所有key,遍历value
        //迭代器
        Set<Integer>keys  =map.keySet();
        Iterator<Integer> it =keys.iterator();
        while (it.hasNext()){
           int a= it.next();
           String s =map.get(a);
            System.out.println("key: "+a+" value: "+s);
        }
        //foreach
        for (Integer i:keys
             ) {
            System.out.println("key: "+i +" value: "+map.get(i));
        }

        //第二种方式,Set<Map.Entry<K,V>> entrySet() 将Map集合转换成set集合
        //这里就是Set<Map.Entry<Integer,String>>
       Set <Map.Entry<Integer,String>>set= map.entrySet();
        Iterator <Map.Entry<Integer,String>>it1=set.iterator();
        while(it1.hasNext()){
        //底层是个node对象,里面有值,键等属性
            Map.Entry<Integer,String> node= it1.next();
            Integer i= node.getKey();
            String s=node.getValue();
            System.out.println("key: "+i+" value: "+s);
        }
        //foreach,效率高
        for (Map.Entry<Integer,String> node:set
             ) {
            System.out.println(node.getKey()+"---"+node.getValue());
        }

    }
}

HashMap
在这里插入图片描述
equals不重写会调用Object上的比较的就是对象地址,要比较内容
hashcode也要重写,如果hashcode()方法返回固定的值,会导致哈希表就变成了纯单向链表了,散列分布不均匀。如果所有的hashcode()方法返回的是不一样的值,这样就导致底层变成数组了,散列分布不均匀。
所以hashcode()方法重写要有技巧。

HashMap的key值也就是set集合
无序:因为算出来的哈希值也就是数组下标不相同,加哪一个链表上是不一定的
不可重重复:equals方法保证了不可重复,如果key重复了,就覆盖了

如果将自定义类的对象放到HashMap集合或者HashSet集合中,两个方法要同时重写,并且equals方法返回是true,hashcode方法返回的值一定相同,因为既然比较为true了,那么一定是比较了,而且还是在同一个链表上,所以哈希值一定相同,经哈希算法计算的数组下标相同

import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Set;

/*
HashMap集合:
    1,HashMap集合底层是哈希表/散列表的数据结构
    2,哈希表是数组和单向链表的结合,一维数组的每个元素是一个单向链表
        Node [] tables;,静态内部类 static class Node {有四个属性,hash(key经过hashcode()方法算出的值经过哈希算法算出的值作为数组的下标),key,value,next}
    3,掌握map.put(k,v)和map.get(k)的实现原理
    4,HashMap默认初始化为16,加载因子是0.75,当底层数组的容量达到百分之七十五的时候开始扩容,初始化容量必须是2的倍数,为了提高存储效率
    5,扩容之后是原来的2倍
    6,HashMap集合允许键值为null,只能是一个
 */
public class HashMapTest02 {
    public static void main(String[] args) {
        //这里的key是Integer类,value是String类,这两个类都重写了equals和hashcode方法
        Map<Integer,String> map=new HashMap<>() ;
        map.put(1,"张三1");
        map.put(2222,"张三2");
        map.put(3,"张三3");
        map.put(3,"张三4");
        Set<Map.Entry<Integer,String>>node=map.entrySet();
        for (Map.Entry<Integer,String> s:node
             ) {
            System.out.println(s.getKey()+"=="+s.getValue());
        }

    }

}

java8之后,如果链表的元素超过8个,单向链表会变成红黑树数据结构。当红黑树上的节点数小于6时,红黑树变成单向链表。也是为了提高检索效率,树有分支,只查一边的。
哈希碰撞:哈希值不相同,经过哈希算法得出的数组下标可能相同。比如取余

Hashtable
键值都不允许是null,线程安全的
底层也是哈希表,初始化容量11,默认加载因子0.75,集合扩容为2倍加1

Properties继承的Hashtable

import java.util.Properties;
/*
Properties的key和value都是String类型
被称为属性类对象
是线程安全的
 */

public class PropertiesTest {
    public static void main(String[] args) {
        Properties p=new Properties();
        p.setProperty("密码","123");
        p.setProperty("密码1","211");
        p.setProperty("密码2","123");

       String s1= p.getProperty("密码");
       String s2= p.getProperty("密码1");
       String s3= p.getProperty("密码2");

        System.out.println(s1);
        System.out.println(s2);
        System.out.println(s3);
    }
}

TreeMap
放进去取出来是有顺序的
无法对自定义类型排序,出现强转错误,是因为自定义Person类没有实现comparable接口
String和Integer都实现了comparable接口,所以可以自己排序
new TreeMap这个集合的时候如果是无参构造还想排序的话得实现接口,还有一种方法是调用有参构造,传进去一个比较器

最终结论:
放到集合TreeSet或者TreeMap集合中key部分的元素如果想做到排序,包括两种方式:
1.放在集合中的元素类实现java.lang.Comparable接口
2.在构造集合的时候,传一个比较器

如果比较规则不发生改变,或者比较规则只有一个,建议第一种
如果频繁改变规则,则传比较器,符合ocp准则

import java.util.TreeSet;

public class TreeSetTest {
    public static void main(String[] args) {
        Person p1=new Person(12);
        Person p2=new Person(11);
        Person p3=new Person(15);

        TreeSet treeSet=new TreeSet();//这里其实有个参数是比较器
        treeSet.add(p1);
        treeSet.add(p2);
        treeSet.add(p3);
    }
}
class Person{
    int age;
    String name;

    public Person() {
    }

    public Person(int age) {
        this.age = age;

    }
}
/*
Exception in thread "main" java.lang.ClassCastException: a.jihe.Person cannot be cast to java.lang.Comparable
	at java.util.TreeMap.compare(TreeMap.java:1294)
	at java.util.TreeMap.put(TreeMap.java:538)
	at java.util.TreeSet.add(TreeSet.java:255)
	at a.jihe.TreeSetTest.main(TreeSetTest.java:12)

重写后
返回负值就是升序
compareTo方法返回值很重要:
返回0会覆盖
返回>0,在右子树上找
返回<0,在左子树上找

/*
放在TreeSet集合中的元素要实现Comparable<Person>接口
并且compareTo实现方法,equals方法可以不写
 */
import java.util.TreeSet;

public class TreeSetTest {
    public static void main(String[] args) {
        Person p1=new Person(12);
        Person p2=new Person(11);
        Person p3=new Person(15);

        TreeSet<Person> treeSet=new TreeSet();//这里其实有个参数是比较器
        treeSet.add(p1);
        treeSet.add(p2);
        treeSet.add(p3);

        for (Person p:treeSet
             ) {
            System.out.println(p);
        }
    }
}
class Person implements Comparable<Person>{
    int age;
    String name;

    public Person() {
    }

    public Person(int age) {
        this.age = age;

    }

    //需要在这规定比较的逻辑,比如按照年龄
    //k.compareTo(t.key)

    @Override
    public int compareTo(Person p) {//p1.compareTo(p2)
        //this就是p1,p就是p2
        int age1=this.age;
        int age2=p.age;
//        if (age1==age2){
//            return 0;
//        }else if (age1>age2){
//            return 1;
//        }else if (age2<age1){
//            return -1;
//        }
        return this.age-p.age;//升序
    }

    @Override
    public String toString() {
        return "Person[age= "+age+" name= "+"]";
    }
}

年龄相同按照姓名排序,实现Comparable<Person>接口的compareTo方法

public int compareTo(Person p) {//p1.compareTo(p2)
        //this就是p1,p就是p2
        int age1=this.age;
        int age2=p.age;
        
        if(age1==age2){
            //年龄相同按照姓名排序
            //姓名是String类型,实现了接口了
            return this.name.compareTo(p.name);
        }else {
            return this.age-p.age;//升序
        }
        
    }

第二种方法,写一个比较器

import java.util.Comparator;
import java.util.TreeSet;

public class TreeSet11 {
    public static void main(String[] args) {
        //创建集合的时候使用下面的比较器
        //括号里面也可以匿名内部类,new Comparator<Wugui>{},是接口,不能直接new,后面接个大括号
        
        TreeSet<Wugui> treeSet =new TreeSet<>(new WuguiComparator());
        Wugui wugui1=new Wugui(12);
        Wugui wugui2=new Wugui(16);
        Wugui wugui3=new Wugui(13);

        treeSet.add(wugui1);
        treeSet.add(wugui2);
        treeSet.add(wugui3);

        for (Wugui w:treeSet
             ) {
            System.out.println(w);
        }

    }
}

class Wugui{
    int age;
    public Wugui (int age){
        this.age=age;
    }

    @Override
    public String toString() {
        return "小乌龟{" +
                "age=" + age +
                '}';
    }
}

//单独写一个比较器
//比较器实现的是java.util.Comparator接口。(Comparable是java.lang包下的)

class WuguiComparator implements Comparator<Wugui> {
    @Override
    public int compare(Wugui o1, Wugui o2) {
        return o1.age-o2.age;
    }
}

自平衡二叉树
遵循左小右大原则存放。
遍历二叉树有三种方式:
前序遍历:根左右
中序遍历:左根右
后序遍历:左右根
根的位置决定前中后

TreeSet集合和TreeMap集合采用的是:中序遍历
Iterator采取的是中序遍历方式

集合工具类Collections

java.util.Collections是工具类,便于集合操作

java.util.Collection是集合接口,想想哪个word文档

import java.util.*;

public class CollectionsTest {
    public static void main(String[] args) {
        List list=new ArrayList();

        //变成线程安全的
        Collections.synchronizedList(list);

        //排序,里面的元素对象的类必须实现Comparable接口
        list.add(123);
        list.add(222);
        list.add(111);
        Collections.sort(list);

        //对Set集合怎么排序,将set转化成list集合
        Set set=new HashSet();
        set.add(1);
        set.add(2);
        List list1=new ArrayList(set);
        
        //Collections.sort(list集合,比较器对象);
        
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值