Java全栈学习day09(容器)

容器
数组就是一种容器,可以在其中放置对象或基本类型数据。

数组的优势:是一种简单的线性序列,可以快速地访问数组元素,效率高。如果从效率和类型检查的角度讲,数组是最好的。
数组的劣势:不灵活。容量需要事先定义好,不能随着需求的变化而扩容。
	        比如:我们在一个用户管理系统中,要把今天注册的所有用户取出来,那么这样的用户有多少个?我们在写程序时是无法确定的。因此,在这里就不能使用数组。

1、泛型(Generics)

jdk1.5之后增加的
  1. 泛型的本质就是“数据类型的参数化”。
//泛型类声明,一个自定义容器
class MyCollection<E> {// E:表示泛型;
    Object[] objs = new Object[5];
 
    public E get(int index) {// E:表示泛型;
        return (E) objs[index];
    }
    public void set(E e, int index) {// E:表示泛型;
        objs[index] = e;
    }
}
//泛型使用
public class TestGenerics {
    public static void main(String[] args) {
        // 这里的”String”就是实际传入的数据类型;
        //jdk1.7之后第二个String可以不写
        MyCollection<String> mc = new MyCollection<String>();
        mc.set("aaa", 0);
        mc.set("bbb", 1);
        String str = mc.get(1); //加了泛型,直接返回String类型,不用强制转换;
        System.out.println(str);
    }
}
  1. 容器中泛型的使用
    容器相关类都定义了泛型,我们在开发和工作中,在使用容器类时都要使用泛型。这样,在容器的存储数据、读取数据时都避免了大量的类型判断,非常便捷。
    定义泛型的接口:
    • Collection
    • List List<String> list = new ArrayList<String>();
    • Set Set<Man> mans = new HashSet<Man>();
    • Map Map<Integer, Man> maps = new HashMap<Integer, Man>();
    • Iterator Iterator<Man> iterator = mans.iterator();

2、Collection接口

Collection 表示一组对象,它是集中、收集的意思。Collection接口的两个子接口是List、Set接口。
Collection接口的方法
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZhomYCYO-1653465408683)(TyporaIMG/Java%E5%85%A8%E6%A0%88%E5%AD%A6%E4%B9%A0day09%EF%BC%88%E5%AE%B9%E5%99%A8%EF%BC%89/1495614959696503.png)]

package com.zry.test01;

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

/**
 * 测试Collection接口中的方法
 * @author ZRY
 *
 */
public class TestList {
	public static void main(String[] args) {
		Collection<String> c=new ArrayList<>();//建立一个String类型的容器
		System.out.println(c.size());//容器尺寸
		System.out.println(c.isEmpty());//判断容器是否为空
		c.add("你好");//向容器中添加元素
		c.add("你是");
		System.out.println(c.size());
		System.out.println(c);
		System.out.println(c.contains("123"));//判断容器中是否有该元素
		Object[] objs=c.toArray();//转化为object数组
		System.out.println(objs);
		c.remove("你是");//移出容器中某个元素
		System.out.println(c);
		c.clear();//清空容器
		System.out.println(c);
	}
}
/**
 * 自定义实现一个ArrayList,体会底层原理
 * @author ZRY
 * @version 1.0
 */
public class ArrayList02<E> {
    private Object[] elementData;
    private int size;
    private static final int DEFALT_CAPACITY=10;
    //空参构造器
    public ArrayList02(){
        elementData=new Object[DEFALT_CAPACITY];
    }
    //构造器
    public ArrayList02(int copacity){
        elementData=new Object[copacity];
    }
    //添加元素方法
    public void  add(E element){
        if(size==elementData.length){
            Object[] newElement=new Object[elementData.length+(elementData.length>>1)];
            System.arraycopy(elementData,0,newElement,0,elementData.length);
            elementData=newElement;
        }

        elementData[size++]=element;
    }
    //删除元素
    public void remove(E element){
        for (int i=0;i<size;i++){
            if (element.equals(get(i))){//容器中所有比较用equals
                remove(i);//移出该位置的元素
            }
        }
    }
    public void remove(int index){
        int numMove=elementData.length-index-1;
        if (numMove>0){
            System.arraycopy(elementData,index+1,elementData,index,numMove);
        }
        elementData[size--]=null;
    }
    @Override
    //打印输出方法
    public String toString() {
        StringBuilder sb=new StringBuilder();
        sb.append("[");
        for (int i=0;i<size;i++){
            sb.append(elementData[i]).append(",");
        }
        sb.setCharAt(sb.length()-1, ']');
        return sb.toString();
    }
    //get方法
    public E get(int index){
        return (E)elementData[index];
    }
    //set方法
    public void set(E element,int index){
        elementData[index]=element;
    }
    //索引越界异常提示
    public void checkRange(int index){
        //判断索引是否合法
        if (index>size-1 || index<0){
            //不合法控制台输出提示信息
            throw new RuntimeException("索引不合法"+index);
        }
    }

    public static void main(String[] args){
        ArrayList02 a1=new ArrayList02(20);
        for (int i=0;i<40;i++){
            a1.add("lll="+i);
        }
        System.out.println(a1.toString());
        a1.remove("lll=5");
        System.out.println(a1.toString());
    }
}


3、数组链表

  • LinkedList底层用双向链表实现的存储.
  • 链表的特点:查询效率低、增删效率高、线程不安全
    在这里插入图片描述
/**
 * @author ZRY
 * @version 1.0
 */
public class LinkedList<E> {//一个自定义的链表类
    private Node first;
    private Node last;
    private int size;
    //向链表里面添加元素
    public void add(E element){
        Node node=new Node(element);
        if (first==null){
            node.previous=null;
            node.next=null;
            first=node;
            last=node;
            size++;
        }else {
            node.previous=last;
            node.next=null;
            last.next=node;
            last=node;
            size++;
        }
    }
    //插入一个元素
    public void add(E element,int index){
        checkRange(index);
        Node newNode=new Node(element);
        Node temp=getNode(index);
        if (temp!=null){
            Node up=temp.previous;
            up.next=newNode;
            newNode.previous=up;
            newNode.next=temp;
            temp.previous=newNode;
        }
        size++;
    }
    //删除一个元素根据索引
    public void remove(int index){
        checkRange(index);
        Node temp= getNode(index);
        if (temp!=null){
            Node up=temp.previous;
            Node down=temp.next;
            if (up!=null)
                up.next=down;
            if (down!=null)
                down.previous=up;
            if(index==0){
                first=down;
            }
            if (index==size-1){
                last=down;
            }
            size--;
        }
    }

    //索引
    private void checkRange(int index){
        if (index<0 || index>size-1){
            throw new RuntimeException("索引数字不合法");
        }
    }
    @Override
    public String toString() {
        StringBuilder sb=new StringBuilder();
        sb.append("[");
        //遍历链表元素
        Node temp=first;
        while (temp!=null){
            sb.append(temp.element+",");
            temp=temp.next;
        }
        sb.setCharAt(sb.length()-1, ']');
        return sb.toString();
    }
    public E get(int index){
        checkRange(index);
        Node temp=getNode(index);
        return temp!=null?(E)temp.element:null;
    }
    private Node getNode(int index){
        Node temp=null;
        if (index<=index<<1){
            temp=first;
            for (int i=0;i<index;i++){
                temp=temp.next;
            }
        }else {
            temp= last;
            for (int i=0;i<index;i++){
                temp=temp.previous;
            }
        }
        return temp;
    }
//测试
    public static void main(String[] args) {
        LinkedList<String> list=new LinkedList<>();
        list.add("a");
        list.add("b");
        list.add("c");
        System.out.println(list);
        list.remove(1);
        System.out.println(list);
        list.add("老号",1);
        System.out.println(list);
    }
}
class Node{
    Node previous;//上一个节点
    Node next;//下一个节点
    Object element;//当前节点
    public Node(Object element){
        this.element=element;
    }
    public Node(Node previous,Node next,Object element){
        this.previous=previous;
        this.next=next;
        this.element=element;
    }
}

Vector

Vector向量
Vector底层是用数组实现的List , 在相关的方法上都加了同步检测 , 线程安全,效率低.

选择使用的条件:

(1) 线程安全时:vector
(2) 不存在线程安全时,查找多:ArrayList
(3) 不存在线程安全问题时,增删频繁:LinkedList

4、Map接口(键-值对)

  • Map接口的实现类:
    (1) HashMap
    (2) TreeMap
    (3) HashTable
    (4) Properties
  • Map常用方法
    在这里插入图片描述
  • HashMap和HashTable的区别:

(1) HashMap:采用哈希算法实现,Map接口最常用的实现类,查找、修改、删除效率高,线程不安全,允许key或value的值为null;
哈希表:数组+链表
在这里插入图片描述
(2) HashTable:相比于HashMap增加了synchronized关键字,确保线程同步,线程安全。不允许key或value的值为null;

  • 手动实现HashMap
package com.test;
/**
 * @author ZRY
 * @version 1.0
 */
public class HashMap01 <K,V>{
    Node01[] table;//位桶数组
    int size;//存放键值对的个数
    public HashMap01(){
        table=new Node01[16];//长度为2的整数次幂
    }
    public V get(K key){
        int hash=myHash(key.hashCode(),table.length);
        V value=null;//初始值
        //开始查找
        if (table[hash]!=null){
            Node01 temp=table[hash];
            while (temp!=null){
                if (temp.key.equals(key)){//如果找到了
                    value= (V) temp.value;
                    break;
                }else {//没找到
                    temp=temp.next;
                }
            }
        }
        return value;
    }
    @Override
    //重写父类的toString方法
    public String toString() {
        StringBuilder sb=new StringBuilder("{");
        for (int i=0;i<table.length;i++){
            Node01 temp=table[i];
            while (temp!=null){
                sb.append(temp.key+":"+temp.value+",");
                temp=temp.next;
            }
        }
        sb.setCharAt(sb.length()-1,'}');
        return sb.toString();
    }
    //未扩容
    public void put(K key, V value){
        //定义新的节点对象
        Node01 newNode=new Node01();
        newNode.hash=myHash(key.hashCode(),table.length);
        newNode.key=key;
        newNode.value=value;
        newNode.next=null;
        Node01 temp=table[newNode.hash];
        Node01 iterLast=null;//正在遍历最后一个元素
        boolean keyRepeat=false;
        if (temp==null){
            //此处数组为空,将新节点直接放入
            table[newNode.hash]=newNode;
            size++;
        }else {
            //此处数组元素不为空,遍历链表
            while (temp!=null){
                //判断key如果重复,则覆盖
                if (temp.key.equals(key)){
                    keyRepeat=true;
                    temp.value=value;//覆盖value
                    break;
                }else{//key不重复
                    iterLast=temp;
                    temp=temp.next;
                }
            }
            if (!keyRepeat){//没有重复
                iterLast.next=newNode;
                size++;
            }
        }
    }
    public int myHash(int v,int length){
        return v&(length-1);//取模运算
    }
}
//-----------------------------------------------
//测试类
class Test{
    public static void main(String[] args) {
        HashMap01<Integer,String > m=new HashMap01();
        m.put(10,"aa");
        m.put(20,"bb");
        m.put(30,"cc");
        m.put(53,"53");
        System.out.println(m.toString());
        System.out.println(m.get(53));

    }
}
//------------------------------------------------
//节点类,用于HashMap01
class Node01<K,V>{
    int hash;
    K key;
    V value;
    Node01 next;
}

5、Set接口

set容器的特点:无序,不可重复.
set常用的实现类: HashSet , TreeSet

6、迭代器介绍

  • 一般遇到容器判断删除情况时使用迭代器遍历
public class Test {
    public static void main(String[] args) {
        Set<String> set = new HashSet<String>();
        for (int i = 0; i < 5; i++) {
            set.add("a" + i);
        }
        System.out.println(set);
        for (Iterator<String> iter = set.iterator(); iter.hasNext();) {
            String temp = iter.next();
            System.out.print(temp + "\t");
        }
        System.out.println();
        System.out.println(set);
    }
}

7、遍历集合方法

  1. 遍历List方法

    • 普通for循环
    • 增强for循环
    • 迭代器
  2. 遍历Set方法

    • 增强for循环
    • 迭代器
  3. 遍历Map方法

    • 迭代器
    • entrySet

8、Collection工具类

1. void sort(List) //对List容器内的元素排序,排序的规则是按照升序进行排序。

  2. void shuffle(List) //对List容器内的元素进行随机排列。

  3. void reverse(List) //对List容器内的元素进行逆续排列 。

  4. void fill(List, Object) //用一个特定的对象重写整个List容器。

  5. int binarySearch(List, Object)//对于顺序的List容器,采用折半查找的方法查找特定对象。

9、练习题

一、选择题
1、以下选项中关于java集合的说法错误的是(AC)

A.List接口和Set接口是Collections接口有两个子接口

B.List接口中存放的元素具有有序,不唯一的特点

C.Set接口中存放的元素具有无序,不唯一的特点

D.Map接口存放的是映射信息,每个元素都是一个键值对

2、如下Java代码,输出的运行结果是( A)。

public class Test {
    public static void main(String[ ] args) {
        List<String> list=new ArrayList<String>();
        list.add("str1");
        list.add(2, "str2");
        String s=list.get(1);
        System.out.println(s);
    }
}
A.运行时出现异常

B.正确运行,输出str1

C.正确运行,输出str2

D.编译时出现异常

3、在Java中,下列集合类型可以存储无序、不重复的数据的是( D)。(选择一项)

A.ArrayList

B.LinkedList

C.TreeSet

D.HashSet

4、以下代码的执行结果是( C)。(选择一项)

Set<String> s=new HashSet<String>();
s.add("abc");
s.add("abc");
s.add("abcd");
s.add("ABC");
System.out.println(s.size());
A.1

B.2

C.3

D.4
  1. 给定如下Java代码,编译运行的结果是( C)。(选择一项)
public class Test {
    public static void main(String[] args) {
        Map<String, String> map = new HashMap<String, String>();
        String s = "code";
        map.put(s, "1");
        map.put(s, "2");
        System.out.println(map.size());
    }
}
A.编译时发生错误

B.运行时引发异常

C.正确运行,输出:1

D.正确运行,输出:2

二、 简答题

  1. 集合和数组的比较。

    • 集合存储的是引用数据类型,大小可变
    • 数组存储基本数据类型和引用数据类型,大小固定
  2. 简述List、Set、Collection、Map的区别和联系。

    • List 和Set是Collection的子接口,Collection接口存储无序,不唯一对象
    • List接口存储有序,不唯一对象
    • Set接口存储无序,唯一对象
    • Map接口存储键值对对象,是key和value的映射关系,key唯一
  3. ArrayList和LinkedList的区别和联系。它们的底层分别是用什么实现的?

    • ArrayList 是数组实现的
    • LinkedList是双向链表实现的
  4. HashSet采用了哈希表作为存储结构,请说明哈希表的特点和实现原理。

    提示:结合Object类的hashCode()和equals()说明其原理。

    系统类已经覆盖了hashCode方法,自定义类如果要放入hash类集合,一般会重写hashCode方法和 equals方法,如果不重写,调用的是Object类的hashCode方法和equals方法,而Object类的hashCode方法返回的是对象的地址,equals方法会默认调用==判断对象的地址是否相同。

  5. 使用泛型有什么好处?
    消除强制类型转化,增强代码复用

三、 编码题

  1. 使用List和Map存放多个图书信息,遍历并输出。其中商品属性:编号,名称,单价,出版社;使用商品编号作为Map中的key。

  2. 使用HashSet和TreeSet存储多个商品信息,遍历并输出;其中商品属性:编号,名称,单价,出版社;要求向其中添加多个相同的商品,验证集合中元素的唯一性。

    提示:向HashSet中添加自定义类的对象信息,需要重写hashCode和equals( )。

向TreeSet中添加自定义类的对象信息,需要实现Comparable接口,指定比较 规则。

  1. 实现List和Map数据的转换。具体要求如下:

    功能1:定义方法public void listToMap( ){ }将List中Student元素封装到Map中

      1) 使用构造方法Student(int id,String name,int age,String sex )创建多个学生信息并加入List;
    
       2) 遍历List,输出每个Student信息;
    
       3) 将List中数据放入Map,使用Student的id属性作为key,使用Student对象信息作为value;
    
       4) 遍历Map,输出每个Entry的key和value。
    

    功能2:定义方法public void mapToList( ){ }将Map中Student映射信息封装到List

       1) 创建实体类StudentEntry,可以存储Map中每个Entry的信息;
    
       2) 使用构造方法Student(int id,String name,int age,String sex )创建多个学生信息,并使用Student的id属性作为key,存入Map;
    
       3) 创建List对象,每个元素类型是StudentEntry;
    
       4) 将Map中每个Entry信息放入List对象。
    

    功能3:说明Comparable接口的作用,并通过分数来对学生进行排序。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值