Java11-泛型和集合

1 泛型

1.1 概念

Java 泛型(generics)是 JDK 5 中引入的一个新特性, 泛型提供了编译时类型安全检测机制,该机制允许程序员在编译时检测到非法的类型。
泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数

1.2 作用

l 通过泛型的语法定义,约束集合元素的类型,进行安全检查,把错误显示在编译期
l 代码通用性更强
l 泛型可以提升程序代码的可读性,但它只是一个语法糖(编译后这样的东西就被删除,不出现在最终的源代码中),对于JVM运行时的性能是没有任何影响的

1.2.1 用途:编译时类型检查

//1,泛型的标志<>
//2,泛型的好处:规定了数据的类型,不能想放什么数据就放什么类型,要遵守泛型规定的类型
//3,泛型的数据类型只能是引用类型,不能是基本类型
List<Integer> list = new ArrayList<Integer>();
list.add(1);
list.add(2);
//4,如果类型不对,把运行时期才会 报的错ClassCastException直接在编译时期就报出来
//     list.add("a");
//     list.add('b');

1.2.2 用途:代码通用性更强

public class TestGenarics {
    public static <E> void print(E[] arr) {
       for(E e : arr) {
           System.out.println(e);
       }
    }

    public static void main(String[] args) {
       Integer[] scores = new Integer[]{ 100,98,80 };
       String[] names = new String[]{ "语文","数学","英语" };
       Double[] moneys = new Double[] { 10.1,20.2,30.3 };
      
       TestGenarics.print(scores);
       TestGenarics.print(names);
       TestGenarics.print(moneys);
    }
}

1.2.3 类型擦除

泛型只是在编译期间生存,编译后就被干掉了,真正运行时,大多情况下取而代之的是Object。

 public static void main(String[] args) throws Exception {
       List<Integer> list = new ArrayList<Integer>();    
       //1. 编译器按泛型检查,类型报错。这是在编译阶段
       //list.add("chenzs");    
       //2. 但在实际运行时,泛型的地方就被替代为通用类型Object
       Class<?> clazz = list.getClass();
       Method m = clazz.getDeclaredMethod("add", Object.class);      
       //3. 利用发射得到的对象是运行时对象,其就可以设置非整形的数据
       m.invoke(list, "chenzs");            
       System.out.println(list.get(0));
    }

1.3 泛型示例

ArrayList使用了泛型,在声明时需指定具体的类型
在这里插入图片描述
有了泛型,我们可以看到要求存放String,而编写时存放了整数100,所以eclipse提示我们错误
类型List的add方法要求增加的类型为String类型,不正确不能存入

1.4 泛型声明

泛型可以在接口/类、方法、返回值上使用

  • java.util.List泛型接口/类
public interface Collection<E> {}
  • 泛型方法的声明
public <E> void print(E e) {}

在方法返回值前声明了一个表示后面出现的E是泛型,而不是普通的java变量

1.5 常用名称

  • E - Element (在集合中使用,因为集合中存放的是元素)
  • T - Type(Java 类)
  • K - Key(键)
  • V - Value(值)
  • N - Number(数值类型)
  • ? - 表示不确定的java类型

2 Collection集合

英文名称Collection,是用来存放对象的数据结构。其中长度可变,而且集合中可以存放不同类型的对象。并提供了一组操作成批对象的方法。
注意:
①、集合只能存放对象。比如你存一个 int 型数据 1放入集合中,其实它是自动转换成 Integer 类后存入的,Java中每一种基本类型都有对应的引用类型。
②、集合存放的是多个对象的引用,对象本身还是放在堆内存中。
③、集合可以存放不同类型,不限数量的数据类型。

  • 概念和继承结构

在这里插入图片描述

  • Collection接口
  -- List接口  : 数据有序,可以重复。    -- ArrayList子类    -- LinkedList子类
  -- Set接口  : 数据无序,不可以存重复值    -- HashSet子类
  • Map接口
    键值对存数据
  -- HashMap
  • 常用方法

boolean add(E e):添加元素。
boolean addAll(Collection c):把小集合添加到大集合中 。
boolean contains(Object o) : 如果此 collection 包含指定的元素,则返回 true。
boolean isEmpty() :如果此 collection 没有元素,则返回 true。
Iterator iterator():返回在此 collection 的元素上进行迭代的迭代器。
boolean remove(Object o) :从此 collection 中移除指定元素的单个实例。
int size() :返回此 collection 中的元素数。
Objec[] toArray():返回对象数组

Collection c = new ArrayList();//接口无法直接创建对象
              c.add("hello");//添加元素
              c.add("~");//添加元素
              c.add(10);//jdk5后有自动装箱功能,相当于把10包装Integer.valueOf(10)
              System.out.println(c.remove("~"));//移除元素
              System.out.println(c.contains("a"));//判断包含关系
              System.out.println(c.size());//集合的长度
              System.out.println(c);     

              //for遍历集合
              for(int i =0 ;i<c.size();i++) {
                     System.out.println(c.toArray()[i]);
              }

              //iterator迭代器遍历
              Iterator it = c.iterator();//对 collection 进行迭代的迭代器
              while (it.hasNext()) {//如果仍有元素,则返回 true
                     System.out.println(it.next());//返回迭代获取到的下一个元素
              }  

2.1 List接口

  • 概述

序的 collection(也称为序列)。此接口的用户可以对列表中每个元素的插入位置进行精确地控制。用户可以根据元素的整数索引(在列表中的位置)访问元素,并搜索列表中的元素

  • 特点

1、 数据有序
2、 允许存放重复元素
3、 元素都有索引

  • 常用方法

ListIterator listIterator()
返回此列表元素的列表迭代器(按适当顺序)。
ListIterator listIterator(int index)
返回列表中元素的列表迭代器(按适当顺序),从列表的指定位置开始。
void add(int index, E element)
在列表的指定位置插入指定元素(可选操作)。
boolean addAll(int index, Collection<? extends E> c)
将指定 collection 中的所有元素都插入到列表中的指定位置(可选操作)。
List subList(int fromIndex, int toIndex)
返回列表中指定的 fromIndex(包括 )和 toIndex(不包括)之间的部分视图。
E get(int index)
返回列表中指定位置的元素。
int indexOf(Object o)
返回此列表中第一次出现的指定元素的索引;如果此列表不包含该元素,则返回 -1。

public static void main(String[] args) {
       //1、创建List对象
       //特点1:List集合元素都有索引,可以根据索引直接定位元素
       List list = new ArrayList();    
       //2、常用方法
       list.add(111);
       list.add(222);
       list.add(333);
       list.add(444);
       list.add('a');
       list.add("abc");
      
       list.add(3,666);//在3的索引处添加指定元素
       //特点2:元素有序, 怎么存就怎么放
       System.out.println(list);//[111, 222, 333, 666, 444, a, abc]
       Object obj = list.get(4);//get(m)-m是索引值,获取指定索引位置的元素
       System.out.println(obj);
      
       //3、迭代/遍历集合中的元素

       //使用Collection接口提供的iterator()
       Iterator it = list.iterator();
       while(it.hasNext()) {//判断集合里有没有下个元素
           Object o = it.next();
//         System.out.println(o);
       }
      
       //使用List接口提供的listIterator()
       //interfaceListIterator extends Iterator
       //区别:可以使用父接口的功能,同时拥有自己的特有功能,不仅顺序向后迭代还可以逆向迭代
       ListIterator it2 = list.listIterator();
       while(it2.hasNext()) {
           Object o = it2.next();
       }   
       System.out.println();

       List list2 = list.subList(1, 3);//subList(m,n)-m是开始索引,n是结束索引,其中含头不含尾类似于String.subString(m,n)
       System.out.println(list2);
    }

2.1.1 ArrayList

概述
  1. 存在于java.util包中。
    
  2. 内部用数组存放数据,封装了数组的操作,每个对象都有下标。
    
  3. 内部数组默认初始容量是10。如果不够会以1.5倍容量增长。
    
  4. 查询快,增删数据效率会降低。
    
创建对象

new ArrayList():初始容量是10

ArrayList扩容

ArrayList相当于在没指定initialCapacity时就是会使用延迟分配对象数组空间,当第一次插入元素时才分配10(默认)个对象空间。假如有20个数据需要添加,那么会分别在第一次的时候,将ArrayList的容量变为10 (如下图一);之后扩容会按照1.5倍增长。也就是当添加第11个数据的时候,Arraylist继续扩容变为10*1.5=15(如下图二);当添加第16个数据时,继续扩容变为15 * 1.5 =22个
ArrayList没有对外暴露其容量个数,查看源码我们可以知道,实际其值存放在elementData对象数组中,那我们只需拿到这个数组的长度,观察其值变化了几次就知道其扩容了多少次

2.1.2 LinkedList

在这里插入图片描述

概述

双向链表,两端效率高。底层就是数组和链表实现的

常用方法

add()
get()
size()
remove(i)
remove(数据)
iterator()
addFirst() addLast()
getFirst() getLast()
removeFirst() removeLast()

测试迭代器遍历

双向链表:下标遍历效率低,迭代器遍历效率高

 long t = System.currentTimeMillis();
              Iterator it = ll.iterator();
              while(it.hasNext()) {
                     it.next();
              }
              t = System.currentTimeMillis()-t;
              System.out.println("=====iterator========="+t);//16



long t = System.currentTimeMillis();
              for (int i = 0; i < ll.size(); i++) {
                     ll.get(i);
              }
              long t1 = System.currentTimeMillis();
              System.out.println("~~~~for~~~~~~~"+(t1-t));//9078

2.2 Set接口

  • 概述
    一个不包含重复元素的 collection
    数据无序(因为set集合没有下标)
    常用于给数据去重
  • 特点
    Ø HashSet:底层是哈希表,包装了HashMap,相当于向HashSet中存入数据时,会把数据作为K,存入内部的HashMap中。当然K仍然不许重复。
    Ø TreeSet:底层就是TreeMap,也是红黑树的形式,便于查找数据。
    Ø HashMap实现中,当哈希值相同的对象,会在同一个hash值的位置存储不同属性的数据
    - 常用方法
    boolean add(E e):添加元素。
    boolean addAll(Collection c):把小集合添加到大集合中 。
    boolean contains(Object o) : 如果此 collection 包含指定的元素,则返回 true。
    boolean isEmpty() :如果此 collection 没有元素,则返回 true。
    Iterator iterator():返回在此 collection 的元素上进行迭代的迭代器。
    boolean remove(Object o) :从此 collection 中移除指定元素的单个实例。
    int size() :返回此 collection 中的元素数。
    Objec[] toArray():返回对象数组

2.2.1 HashSet

概述
此类实现 Set 接口,由哈希表(实际上是一个 HashMap 实例)支持。它不保证 set 的迭代顺序;特别是它不保证该顺序恒久不变。此类允许使用 null 元素

  • 特点
    不能保证元素的顺序
    不可重复
    不是线程安全的
    集合元素可以为 NULL

2.2.2 LinkedHashSet

不可以重复,有序
因为底层采用 链表 和 哈希表的算法。链表保证元素的添加顺序,哈希表保证元素的唯一性

2.2.3 TreeSet

有序;不可重复,底层使用 红黑树算法,擅长于范围查询

3 Map

  • 概述
    key-value 的键值对,key 不允许重复,value 可以
  • 特点
    Ø 可以根据键 提取对应的值
    Ø 键不允许重复,如果重复值会被覆盖
    Ø 存放的都是无序数据
    Ø 初始容量是16,默认的加载因子是0.75
    在这里插入图片描述
  • 常用方法
    void clear()
    从此映射中移除所有映射关系(可选操作)。
    boolean containsKey(Object key)
    如果此映射包含指定键的映射关系,则返回 true。
    boolean containsValue(Object value)
    如果此映射将一个或多个键映射到指定值,则返回 true。
    V get(Object key)
    返回指定键所映射的值;如果此映射不包含该键的映射关系,则返回 null。
    boolean isEmpty()
    如果此映射未包含键-值映射关系,则返回 true。
    V put(K key, V value)
    将指定的值与此映射中的指定键关联(可选操作)。
    void putAll(Map<? extends K,? extends V> m)
    从指定映射中将所有映射关系复制到此映射中(可选操作)。
    V remove(Object key)
    如果存在一个键的映射关系,则将其从此映射中移除(可选操作)。
    int size()
    返回此映射中的键-值映射关系数。
    Set<Map.Entry<K,V>> entrySet()
    返回此映射所包含的映射关系的 Set 视图

3.1 HashMap

HashMap的键要同时重写hashCode()和equals()
hashCode()用来判断确定hash值是否相同
equals()用来判断属性的值是否相同
– equals()判断数据如果相等,hashCode()必须相同
– equals()判断数据如果不等,hashCode()尽量不同

  • 概述
    基于哈希表的 Map 接口的实现。此实现提供所有可选的映射操作,并允许使用 null 值和 null 键
    HashMap底层是一个Entry数组,当存放数据时会根据hash算法计算数据的存放位置。算法:hash(key)%n,n就是数组的长度。
    当计算的位置没有数据时,就直接存放,当计算的位置有数据时也就是发生hash冲突的时候/hash碰撞时,采用链表的方式来解决的,在对应的数组位置存放链表的头结点。对链表而言,新加入的节点会从头结点加入
    在这里插入图片描述
  • 读取HashMap的数据
 public static void main(String[] args) {
              HashMap map = new HashMap ();
              map.put(100, "刘德华");
              map.put(101, "梁朝伟");
              map.put(102, "古天乐");
              map.put(103, "周润发");
//遍历方式1:keySet ()
			  Set set = m.keySet();
              Iterator it = set.iterator();
              while(it.hasNext()) {
                     String key = (String) it.next();
                     String val = (String) m.get(key);
                     System.out.println(key+"="+val);
              }     

              //遍历方式2:entrySet ()
			  Set set2 = m.entrySet();
              Iterator it2 = set2.iterator();
              while(it2.hasNext()) {
                     Entry en = (Entry) it2.next();
                     String key = (String) en.getKey();
                     String value = (String) en.getValue();
                     System.out.println(key+"=="+value);
              }
       }

HashMap扩容

成长因子:
static final float DEFAULT_LOAD_FACTOR = 0.75f;
前面的讲述已经发现,当你空间只有仅仅为10的时候是很容易造成2个对象的hashcode 所对应的地址是一个位置的情况。这样就造成 2个 对象会形成散列桶(链表)。这时就有一个加载因子的参数,值默认为0.75 ,如果你hashmap的 空间有 100那么当你插入了75个元素的时候 hashmap就需要扩容了,不然的话会形成很长的散列桶结构,对于查询和插入都会增加时间,因为它要一个一个的equals比较。但又不能让加载因子很小,如0.01,这样显然是不合适的,频繁扩容会大大消耗你的内存。这时就存在着一个平衡,jdk中默认是0.75,当然负载因子可以根据自己的实际情况进行调整。

4 Collections工具类

Collections.sort(List<> list):根据元素的自然顺序 对指定列表按升序进行排序
Collections.max():根据元素的自然顺序,返回给定 collection 的最大元素
Collections.min():根据元素的自然顺序 返回给定 collection 的最小元素
Collections.swap(List,i,j):在指定列表的指定位置处交换元素
Collections.addAll();

public static void main(String[] args) {

              List<String> list = new ArrayList();           
              //添加多个元素
              Collections.addAll(list,"3","30","23","15","29","12","26");      
              //元素排序
              Collections.sort(list);
         	  //默认是字符顺序:[12,15,23,26,29, 3, 30]
              System.out.println(list);          
              //自己定义比较方式
              Collections.sort(list, new Comparator<String>() {
                     //自定义比较器,sort()自动调用
                     @Override
                     public int compare(String o1, String o2) {
                            //把字符串转成int比大小
                            int a = Integer.parseInt(o1);
                            int b = Integer.parseInt(o2);
					//o1大是正数,o1小是负数,相等是0
                            return a-b;
                     }
              });
              System.out.println(list);
       }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值