zoukankan      html  css  js  c++  java
  • javaday19_List接口_Set接口

    List接口:

    它的父类是 Collections

    List接口派系的三大特点:

    它下面的所有子类都是具有这些特性的。

    List接口的自己的特有方法:

    前面说过,List 接口的特点是带索引,所以 API文档中,看见带有索引的方法一般都是它自己特有的方法。

     1 package cn.zcb.demo01;
     2 import java.util.ArrayList;
     3 import java.util.List;
     4 
     5 public class Test {
     6     public static void main(String[] args) {
     7         List<Integer> arrayList = new ArrayList<>(); //多态调用。
     8 
     9         for (int i=0;i<5;i++){
    10             arrayList.add(i);
    11         }
    12         System.out.println(arrayList);
    13 
    14         //List接口特有方法1:void add(int idx,T e);
    15         arrayList.add(1,15);//在 idx =1 处加上15
    16         System.out.println(arrayList);
    17 
    18         //List接口特有方法2:E get(int idx)
    19         int a =  arrayList.get(2);
    20         System.out.println(a);
    21 
    22         //List接口特有方法3:E remove(int idx)
    23         int a2 = arrayList.remove(0);
    24         System.out.println(arrayList);
    25         System.out.println("被删除的元素是 "+a2);
    26 
    27         //List接口特有方法4:E set(int idx,E);
    28         int ret = arrayList.set(0,1);
    29         System.out.println(arrayList);
    30         System.out.println("被修改的元素是:"+ret);
    31 
    32     }
    33 }
    View Code

    注:带有索引的操作都要防止索引越界产生异常。

    List接口的实现类对象的三种遍历方式:

    1,集合的通用模式: 迭代器方式。

    2,使用for循环 索引idx 并结合 E get(int idx);
    3, 使用增强for循环

     1 package cn.zcb.demo01;
     2         import java.util.ArrayList;
     3         import java.util.Iterator;
     4         import java.util.List;
     5 
     6 public class Test {
     7     public static void main(String[] args) {
     8         List<Integer> arrayList = new ArrayList<>(); //多态调用。
     9 
    10         for (int i=0;i<5;i++){
    11             arrayList.add(i);
    12         }
    13         /*List三种遍历模式
    14          * */
    15         //1 使用迭代器(集合通用的方法)
    16         Iterator<Integer> iterator = arrayList.iterator(); //arrayList.iterator() 的返回值是 Iterator接口的实现类对象。 这里直接用Iterator来接收,发生了多态。
    17         while (iterator.hasNext()){
    18             //这里的一个操作 .next() 返回一个值,并将指针往后挪一个。
    19             int ret =  iterator.next();
    20             System.out.print(ret);
    21         }
    22 
    23         //2 使用for循环 + 索引 + get(int idx)
    24         for (int i=0;i< arrayList.size();i++){
    25             System.out.print(arrayList.get(i));
    26         }
    27         //3 增强for 循环
    28         for (int i:arrayList){
    29             System.out.print(i);
    30         }
    31     }
    32 }
    List接口的实现类对象的三种遍历方式!

    如果只是单单遍历的话,推荐使用增强for 循环,简单好用。

    迭代器的并发修改异常:

    其实就是当使用迭代器遍历的过程中,我们却修改了集合中存储的长度!。

    例如如下代码:就出现了并发修改异常:

     1 package cn.zcb.demo01;
     2 import java.util.ArrayList;
     3 import java.util.Iterator;
     4 import java.util.List;
     5 
     6 public class Test {
     7     public static void main(String[] args) {
     8         List<Integer> arrayList = new ArrayList<>(); //多态调用。
     9 
    10         for (int i=0;i<5;i++){
    11             arrayList.add(i);
    12         }
    13         Iterator <Integer> iterator = arrayList.iterator();
    14         while (iterator.hasNext()){
    15             int ret = iterator.next();
    16             System.out.println(ret);
    17             if(ret == 3){
    18                 arrayList.add(10);
    19             }
    20         }
    21     }
    22 }
    View Code

    注: 如果使用set修改某个元素的值也可以。但是就是不能修改长度!!!长度不能变

    补充:

    我们平时用来比较两个东西是否相等,

    对于基础类型之间的比较,我们可以使用 == 

    但是对于引用类型的比较,我们应该使用 .equals()  方法。

    List接口下实现类的数据存储 的结构:

    List 接口下有很多个实现类。它们存储元素采用的结构方式是不同的,这样就导致了这些集合有它们各自的特点,供我们在不同的环境下使用。

    数据存储的常用结构有:堆栈,队列,数组,链表。

    1,堆栈:

    2,堆栈:

    3,数组:

    4,链表:

    List接口的实现类--ArrayList 类:

    它是List接口的一个实现类,它的底层实现是用数组来做的。

    ArrayList自身的特点:

    它是一个可变长的数组。

    查询快,增删慢。  

    List接口的实现类--LinkedList 类:

    它也是List 的一个实现类,它的底层是用链表(单向链表) 来实现的。

    LinkedList自身的特点:

    增删快,查询慢。

    主要是对首尾进行操作。 

    LinkedList特有的方法 :

    代码省略。

    List接口的实现类--Vector 类:

    它基本上和ArrayList 一样,只不过它是同步的,是安全的(速度慢)。

    而ArrayList 是异步,(速度快)。(虽然不安全),所以Vector基本已经被ArrayList取代了。

    补充:通常速度快的不安全,速度慢的安全。

    Set接口:

    Set接口介绍:

     它的遍历方式只有两种:

    1,迭代器,

    2,增强for 

    没有索引的遍历方式。  

    Set接口中的特有方法:

    它几乎和它的父亲Collections接口中的方法一模一样,所以没有什么它独有的方法可看。

    Set接口的实现类--HashSet类:

    底层数据结构是哈希表。不同步,运行速度快。增删和查询都比较快

    它由哈希表(实际上是一个HashMap实例)支持。

    集合分两大接口,Interable和 Map 两个

    所以,学了HashSet 就相当于学了HashMap 。 

    HashSet 类的特点:

    代码:

     1 package cn.zcb.demo01;
     2 import java.util.HashSet;
     3 import java.util.Iterator;
     4 
     5 public class Test {
     6     public static void main(String[] args) {
     7         /*Set接口的实现类 HashSet(哈希表) 的使用*/
     8         HashSet<String> hashSet = new HashSet();
     9 
    10         for (int i=0;i<5;i++){
    11             hashSet.add("a"+i);
    12         }
    13         //使用迭代器遍历 hashSet
    14         Iterator<String> iterator = hashSet.iterator();
    15         while (iterator.hasNext()){
    16             String ret = iterator.next();
    17             System.out.println(ret);
    18         }
    19     }
    20 }
    View Code

    注:它的输出顺序和 代码中存储的顺序无关。

    它是无序的。

    哈希表的数据结构:

    哈希表是 链表,数组的结合体!!!所以它 增删元素(链表) 和 查询元素(数组)都比较快。

    这里的桶是个比喻,指的是链表的节点。

    初始容量:指的是数组的长度,默认是16. 

    加载因子指的是:当数组中的实际长度达到容量的百分比之后,就会自动扩容了。所以加载因子不能太小。

    当比例达到加载因子的时候,数组就会进行扩容了,这个动作叫做 再哈希 ,rehash()。  

    对象的哈希值:

    它就是个普通的十进制值。

    它是父类Object中的一个方法 public int hashCode(); 

    正因为它是Object 中的一个方法,所以所有的对象都是可以调用它的。

    如下代码:

    1 package cn.zcb.demo01;
    2 
    3 public class Person {
    4 
    5 
    6 }
    Person.java
    1 package cn.zcb.demo01;
    2 
    3 public class Test {
    4     public static void main(String[] args) {
    5         Person person = new Person();
    6         System.out.println(person.hashCode());
    7     }
    8 }
    Test.java

    .hashCode() 它的源码我们不能看到。

    但是因为它不是final ,也不是private ,所以我们可以重写它。

    重写:

    1 package cn.zcb.demo01;
    2 
    3 public class Person {
    4 
    5     public int hashCode(){
    6         return 0;
    7     }
    8 }
    Person.java
    1 package cn.zcb.demo01;
    2 
    3 public class Test {
    4     public static void main(String[] args) {
    5         Person person = new Person();
    6         System.out.println(person.hashCode());
    7     }
    8 }
    Test.java

    hash值是HashSet 中存储数据的依据。   

    String类 重写了Object的方法 .hashCode():

    前面是我们自己的类 重写了.hashCode()  ,下面看String如何重写的。

    原因是:String类继承了Object 类,它重写了Object 的方法hashCode(),它有它自己的计算hash值的方法。所以二者的hash值一样。

     1 package cn.zcb.demo01;
     2 
     3 public class Test {
     4     public static void main(String[] args) {
     5 
     6         String s1 = new String("abc");
     7         String s2 = new String("abc");
     8         String s3 = new String("abc");
     9         System.out.println(s1.hashCode());
    10         System.out.println(s2.hashCode());
    11         System.out.println(s3.hashCode());
    12     }
    13 }
    View Code

    以上就是String重写之后的源码。 

    下面分析它:

    注:

    hash 默认是0  

    value 指的就是我们的对象 即调用这个方法的对象。  而且value 是个字符数组,我们知道String对象底层是个char [] 数组,这里的value 就是char [] 数组。

    所以,String类对象的hash 值是自己算的。没有使用其父类Object 中的方法.hashCode()。

    hash表的存储过程:

    此时哈希表中只存了两个。

    第一步:

    第二步:

    如果没有的话,就存入。

    如果有相同的哈希值,  然后后面这个 要再调用.equals()  。

    总结: 

    对象在存入哈希表的时候,

    首先会看是否存在哈希值。

    如果没有存在,直接存入。

    如果已经有了哈希值。进入第二步程序---equals() 判断。

    如果判断equals()相同的话,说明对象重复,最终结果不存入。

    如果判断equals()不相同的话,说明对象不重复,将后来这个对象挂到已有对象的下面(桶的存储方式)。

    注:不同的对象的hash值可能会相同的。  

    hash表存储自定义的对象:

     1 package cn.zcb.demo01;
     2 
     3 import java.util.HashSet;
     4 
     5 public class Test {
     6     public static void main(String[] args) {
     7         HashSet<Person> hashSet = new HashSet<>();
     8         Person p1 = new Person("tom",18);
     9         Person p2 = new Person("egon",20);
    10         Person p3 = new Person("alex",32);
    11         Person p4 = new Person("jane",38);
    12         Person p5 = new Person("alex",32);
    13         hashSet.add(p1);
    14         hashSet.add(p2);
    15         hashSet.add(p3);
    16         hashSet.add(p4);
    17         System.out.println(hashSet);
    18         //此时如果 再次存入  tom 18
    19         hashSet.add(p5);
    20         System.out.println(hashSet);
    21 
    22     }
    23 }
    View Code
     1 package cn.zcb.demo01;
     2 
     3 import java.util.HashSet;
     4 
     5 public class Test {
     6     public static void main(String[] args) {
     7         HashSet<Person> hashSet = new HashSet<>();
     8         Person p1 = new Person("tom",18);
     9         Person p2 = new Person("egon",20);
    10         Person p3 = new Person("alex",32);
    11         Person p4 = new Person("jane",38);
    12         Person p5 = new Person("alex",32);
    13         hashSet.add(p1);
    14         hashSet.add(p2);
    15         hashSet.add(p3);
    16         hashSet.add(p4);
    17         System.out.println(hashSet);
    18         //此时如果 再次存入  tom 18
    19         hashSet.add(p5);
    20         System.out.println(hashSet);
    21 
    22         /*分析上述结果*/
    23         System.out.println(p1.hashCode());
    24         System.out.println(p2.hashCode());
    25         System.out.println(p3.hashCode());
    26         System.out.println(p4.hashCode());
    27         System.out.println(p5.hashCode());
    28         /*发现它们的hashCode 都是不同的,所以都可以存入哈希表了*/
    29 
    30 
    31 
    32     }
    33 }
    分析上述代码的结果。

    现在的需求是解决这个事情,使得 相同名字和年龄的对象 不能存入。

     1 package cn.zcb.demo01;
     2 
     3 public class Person {
     4     public String name;
     5     public int age;
     6 
     7     public Person(String name,int age){
     8         this.name = name;
     9         this.age= age;
    10     }
    11     Person(){}
    12 
    13     @Override
    14     public String toString(){
    15         return this.name +" " +this.age+" ";
    16     }
    17 
    18     @Override
    19     public int hashCode(){
    20         return 1;  //所有的对象的hashCode 都相同,
    21     }
    22 //    Object
    23     //重写 Object 中的 equals方法
    24     @Override
    25     public boolean equals(Object obj){
    26         if(obj == null)
    27             return false;
    28         if(obj instanceof Person) {
    29             Person p = (Person) obj;
    30             return this.name.equals(p.name) && this.age == p.age;
    31         }
    32         return false;
    33     }
    34 
    35 }
    Person.java
     1 package cn.zcb.demo01;
     2 
     3 import java.util.HashSet;
     4 
     5 public class Test {
     6     public static void main(String[] args) {
     7         HashSet<Person> hashSet = new HashSet<>();
     8 
     9         hashSet.add(new Person("a",10));
    10         hashSet.add(new Person("b",10));
    11         hashSet.add(new Person("c",10));
    12         hashSet.add(new Person("d",10));
    13         hashSet.add(new Person("a",10));
    14 
    15         System.out.println(hashSet);
    16 
    17 
    18     }
    19 }
    Test.java

    但是,上面的代码是不行的,因为它每次返回的hash 值都是相同的,因此程序每次都会调用equals() ,这样效率是不高的。我们应该降低hash值相同的概率。这样效率才会上去。

     1 package cn.zcb.demo01;
     2 
     3 public class Person {
     4     public String name;
     5     public int age;
     6 
     7     public Person(String name,int age){
     8         this.name = name;
     9         this.age= age;
    10     }
    11     Person(){}
    12 
    13     @Override
    14     public String toString(){
    15         return this.name +" " +this.age+" ";
    16     }
    17 
    18     @Override
    19     public int hashCode(){
    20         return this.name.hashCode() +this.age;   //此时,hash值 相同的概率就会降低了
    21     }
    22 //    Object
    23     //重写 Object 中的 equals方法
    24     @Override
    25     public boolean equals(Object obj){
    26         if(obj == null)
    27             return false;
    28         if(obj instanceof Person) {
    29             Person p = (Person) obj;
    30             return this.name.equals(p.name) && this.age == p.age;
    31         }
    32         return false;
    33     }
    34 
    35 }
    Person.java

    但是,它还是会有相等的情况。例如: name="a" age =10 和 name="b" age =9 

    我们可以再次降低hash值相同的概率:给age 乘上一个不是0 不是1 的任何数都可以降低概率。

     1 package cn.zcb.demo01;
     2 
     3 public class Person {
     4     public String name;
     5     public int age;
     6 
     7     public Person(String name,int age){
     8         this.name = name;
     9         this.age= age;
    10     }
    11     Person(){}
    12 
    13     @Override
    14     public String toString(){
    15         return this.name +" " +this.age+" ";
    16     }
    17 
    18     @Override
    19     public int hashCode(){
    20         return this.name.hashCode() +this.age*55;   //此时,hash值 相同的概率会 再次降低的。
    21     }
    22 //    Object
    23     //重写 Object 中的 equals方法
    24     @Override
    25     public boolean equals(Object obj){
    26         if(obj == null)
    27             return false;
    28         if(obj instanceof Person) {
    29             Person p = (Person) obj;
    30             return this.name.equals(p.name) && this.age == p.age;
    31         }
    32         return false;
    33     }
    34 
    35 }
    hash值相同的概率,会再次降低的。

    注:此时,即便两个不同的对象hash值相等了,它们也仅仅是突破了第一道关卡,后面还有终极boss ,equals()等着呢!!!

    Set接口的实现类--LinkedHashSet类:

    它是继承自 HashSet 类.。 肯定也是Set接口的实现类。 

    它的特点是:无索引,有序,不可重复。

    它的父类 HashSet 的特点是:无索引,无需,不可重复,

    ArrayList 的特点是:索引,有序,可重复。

     1 package cn.zcb.demo01;
     2 
     3 import java.util.LinkedHashSet;
     4 
     5 public class Test {
     6     public static void main(String[] args) {
     7 
     8         LinkedHashSet<String> linkedHashSet = new LinkedHashSet<>();
     9         linkedHashSet.add("tom");
    10         linkedHashSet.add("egon");
    11         linkedHashSet.add("alex");
    12         linkedHashSet.add("jane");
    13         linkedHashSet.add("egon");
    14 
    15         System.out.println(linkedHashSet);
    16 
    17     }
    18 }
    它和它父类的区别是 它是有序的。

    判断集合元素唯一的原理:

    add是 有返回值的。在HashSet 中add也可以用于判断是否有元素的重复。

    补:

    Java 中的八种基本类型的包装类 基本都实现了 hashCode()  和 equals() 方法,为的就是放入Set中的时候,不放入重复的元素。

    所以,对于我们的自定义类型,如果想要去掉重复的元素放入Set中,就要重写.hashCode()和 equals() 方法。

    面试题:

    第二个之所以是一定,因为规定了如果equals() 相等的话,那么.hashCode()要相等。

    但是,但equals()不相等,则不作要求。

    总结:equals()是终极boss ,我tm equals()都相等了,你一个小小的hashCode ()还想搞什么卵蛋。

  • 相关阅读:
    消失的 unittest.makeSuite()
    自定义错误信息在各个浏览器表现不同
    “创建Web应用程序”与“创建Web网站”的区别
    如何将Notepad++改造成Python开发工具
    64位服务器IIS不能识别32位framework版本。IIS没有Asp.net切换界面的解决办法。
    C++文件流,读入数据注意
    Chessboard(二分图完备匹配)
    courses二分图最大匹配
    Linux BASH Shell文件名匹配/输出重定向
    LinuxShell一些很少用到却很有用的指令
  • 原文地址:https://www.cnblogs.com/zach0812/p/11873062.html
Copyright © 2011-2022 走看看