泛型方法、初始集合和集合的遍历

一、泛型方法

1.什么是泛型方法

语法格式:

【修饰符】<泛型的类型> 返回值类型 方法名(形参列表)[throws异常列表]{}

例如:public void method (T t){

}

3,为什么有泛型方法

(1)对于静态方法来说那我们之前声明的<泛型>是不能用于静态成员,所以如果某个静态方法需要用到泛型,那么可以考虑使用泛型方法

(2)类或者接口已经声明,不是泛型类后新增的方法可以使用泛型

不管是(1)还是(2)都有一个原因,因为在设计这个方法的时候,可能它的形参类型或返回值类型未知。这个时候考虑使用泛型方法

4.注意

(1)泛型方法的<泛型类型形参列表>同样可以声明上限

<T extends 上限1 & 上限2 …>

上限中类只能有一个,必须在左边,接口可以有多个

多个上限之间是&的关系

(2)泛型方法的<泛型类型形参列表>必须写在返回值类型前面,修饰符的后面

(3)泛型方法的泛型类型不用手动指定,会根据实参的类型自动推断形参的类型。

二、泛型通配符

1.长什么样子

(1)<?>: ?代表任意类型

(2)<? extends xx>

(3)<? super xx>

2、作用

(1)声明一个变量时,该变量的类型如果是一个泛型类或泛型接口,呢么可以加通配符,例如:Student 类

声明变量student<?> stu;

(2)声明一个形参时,该形参的类型如果是一个泛型类或泛型接口,那么可以加通配符,

例如:public [static ] void method(Student <?> stu){}

(3)注意;不能用在声明泛型的位置

public class MyClass <?>{}

public static <?> void method(){}

3、为什么要有通配符?
(1)我们在使用泛型类或泛型接口时,除非泛型被擦除了,否则你指定了什么类型,就必须是什么类型。
如果希望是任意类型,又不想泛型擦除,只能用<?>。

public class TestWildCard {
    //需求一:声明一个方法,该方法,可以接受任意一种学生对象,至于方法的功能我们暂时先不管
    /*public void method1(Student stu){//写法一:可以,但是没有指定泛型,相当于泛型擦除
        //...
    }*/
    public void method2(Student<Object> stu){//写法二:有问题,只能接收new Student<Object>();
        //...
    }

    public void method3(Student<?> stu){//写法三:可以,这个?代表的就是任意类型
        //...
    }

    @Test
    public void test01(){
        method2(new Student<Object>());
//        method2(new Student<String>());//错误的
//        method2(new Student<Integer>());//错误的
    }

    @Test
    public void test02(){
        method3(new Student<Object>());
        method3(new Student<String>());
        method3(new Student<Integer>());
    }

    //需求2:用泛型类Student,声明一个数组,该数组中可以存储任意学生对象
    @Test
    public void test03(){
//        Student<Object>[] arr = new Student[3];
        Student<?>[] arr = new Student[3];
        arr[0] = new Student<Object>();
        arr[1] = new Student<String>();
        arr[2] = new Student<Integer>();
    }
}
class Student<T>{
    private String name;
    private T score;

    public Student(String name, T score) {
        this.name = name;
        this.score = score;
    }

    public Student() {
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public T getScore() {
        return score;
    }

    public void setScore(T score) {
        this.score = score;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", score=" + score +
                '}';
    }
}

泛型通配符<?>等形式有局限性:
如果某个泛型类或泛型接口使用时,用到<?>等形式,该类型对应的变量修改值就受到了限制。

三、集合

1.容器

java中容器:数组(可以装各种基本数据类型的值和对象)、

集合:可以装各种对象,不能装基本数据类型的值

分好多中:列表list,集:set,队列Queue:,映射:Map…

2.有数组还要有集合?

(1)数组:

优点:速度快,可与根据下标定位元素

缺点:长度是固定的,

数组要求开辟一整块连续的空间GC回收导致性能降低

(2)集合

优点:类型很多,表示我们的可选性多,可以根据需求选择合适的集合;集合的底层可以自动扩容

缺点:类型很多,需要了解每一种类型的特点,好选择

3、集合的分类

(1)collection系列:包括列表List,集Set,队列Queue等

存储一组对象,比喻:party

(2)Map系列

存储键值对(映射关系),(key,value),比喻

3.1collection

1.java.util.collection接口

(1)层次结构中的跟接口

(2)表示一组对象

(3)一些collection允许有重复的元素,另一些不允许

一些有序,一些无序。

(4)没有类能直接实现collection接口,可以实现子接口(list、Set)

(5)此接口通常用来传递collection ,并在需要最大普遍性的地方操作这些collection.

即如果我需要List,Set等集合之间转换的时候,可以使用Collection作为桥梁进行传递,说白了就是多态引用

2.collection的常用方法

他下面的子接口的实现类都有该接口的方法

代表了集合的通用操作:增、查、删除等操作

Collection泛型接口

之前用T,代表Type,通用类型

现在用E代表元素的类型,

(1)添加

一次添加一个元素

boolean add(E e) 一次添加多个元素

boolean addAll(Collection<? extends E> c) :一次添加多个元素

<? extends E>:当前集合的元素是E,c集合的元素类型要求是E或E的子类对象。 ###### (2)删除元素 void clear() : 清空 boolean remove(Object o):删除一个元素 用Object的原因,早期没有泛型,后期有了之后没改,添加的时候要检查类型是否符合,删除时不用。 boolean removeAll(Collection <?> c): this当前集合 = this当前集合 - this当前集合 ∩ c集合 <?>表示任意类型
(3)修改:没有提供修改元素的方法
(4)查询

int size() 获取元素的个数

boolean isEmpty() : 判断是否是空集合

boolean contains(Object o):是否包含某个对象、

boolean containsAll(Collection<?> c):是否包含c集合的所有元素

返回true,c是this集合的子集

5.其他

boolean retainAll(Collection<?> c)保留this集合和c集合的交集,即this集合 = this集合 ∩ c集合、

Object[] toArray() :把集合中的元素放到一个Object数组中返回
T[] toArray(T[] a):把集合的元素放到a数组中,但是a数组的类型没有检查是否符合当前集合的元素E类型。用的时候谨慎。

 public void test01(){
//        Collection coll = new Collection() ;//错误,接口不能直接new对象
        Collection<String> coll = new ArrayList<>() ;//ArrayList是List子接口的实现类,它也是Collection系列。
                                            //多态引用,coll编译期间只能看Collection里面的方法,关注点更集中。
                                            //<String>代表集合的元素类型是String
        coll.add("hello");
        coll.add("world");
        coll.add("java");

        System.out.println(coll);//说明ArrayList实现类重写类toString方法
    }
二、集合的便利
1、Collection 系列的集合有一个方法:

Iteratoriterator()

1、Collection系列的集合支持foreach便利

foreach:称为增强for循环,比普通的for循环简洁,功能更强大,可以直接遍历数组、Collection系列的集合等等,不需要下标等信息。
语法格式:
for(元素的类型 元素名 : 数组/集合的名称){
}
注意:元素名是自己命名的,只是一个临时名称,在当前for循环中使用的临时变量。

问题1:foreach是如何工作的?
foreach其实本质上就是Iterator迭代器。

问题2:数组为什么支持呢?
数组这个类型是编译器动态生成的类型,它在生成的过程中,也会提供迭代器的实现。

问题3:是否所有的集合,或者容器都支持foreach迭代器呢?
不是。
只有实现了java.lang.Iterable接口。
实现这个Iterable接口允许对象成为 “foreach” 语句的目标。
因为Iterable接口的抽象方法是 Iterator iterator() 。如果要实现这个方法,就要手动实现迭代器,为foreach的运行提供基础。

问题4:在foreach遍历集合的过程中,能不能调用集合的remove方法呢?
不能

结论:根据条件删除元素,只有一种方式,用Iterator迭代,并且用 Iterator迭代器的remove方法。
如果精确删除,就直接调用集合remove方法即可。

Collection系列的集合有一个方法:

itertor()

java.util.Iterator迭代接口,有三个方法:

(1)boolean hasNext():判断是否还有下一个元素

(2)E next(): 取出下一个元素

(3)void remove():删除刚刚迭代的元素

Collection的remove适合用于精确删除

迭代器中的无法精确的说明元素时用迭代器的remove方法

public class TestIterator {
    @Test
    public void test03() {
        Collection<String> coll = new ArrayList<>();
        coll.add("hello");
        coll.add("world");
        coll.add("java");
        coll.add("java");
        Iterator<String> iterator = coll.iterator();
        while(iterator.hasNext()) {
            String element = iterator.next();
            if (element != null && element.startsWith("j")) {
                coll.remove(element);
                //报ConcurrentModificationException:并发修改异常。  两条线路在同时访问与修改集合,一个是iterator,一个是coll
                //你的coll的remove方法删除了元素之后,没有把最新的信息告知iterator
            }
        }
        System.out.println(coll);
    }
    @Test
    public void test02(){
        Collection<String> coll = new ArrayList<>();
        coll.add("hello");
        coll.add("world");
        coll.add("java");

        //需求:删除以w开头的字符串
        /*
        思考:用Collection接口的remove方法能否完成?
         */
//        coll.remove();//无法完成,即Collection的remove适用于精确删除
        //使用Iterator迭代器删除,标准写法
        Iterator<String> iterator = coll.iterator();
        while(iterator.hasNext()){
            String element = iterator.next();
            if(element!=null && element.startsWith("w")){
                iterator.remove();
            }
        }
    }

    @Test
    public void test01(){
        Collection<String> coll = new ArrayList<>();
        coll.add("hello");
        coll.add("world");
        coll.add("java");

        Iterator<String> iterator = coll.iterator();
 /*       System.out.println(iterator.next());
        System.out.println(iterator.next());
        System.out.println(iterator.next());
        System.out.println(iterator.next());//NoSuchElementException:没有这个元素了*/

        while(iterator.hasNext()){
            System.out.println(iterator.next());
        }
    }
}

四、Set接口

1.java.util.Set

一个不包含重复元素的collection,对数学中的集的抽象

2.Set时Collection子接口

,但是Set接口没有增加自己的方法和Collection的方法一样

3.Set接口有一些实现类:
(1)HashSet:

不能重复,无序(和添加顺序不一致)

根据hashCode和equals方法。

hashCode值,影响他的储存位置

(2)TreeSet:

元素也不能重复。
元素有大小顺序的,说明元素要支持比较大小,要求元素必须实现java.lang.Comparable接口。
如果元素没有实现Comparable接口,或者实现这个接口的比较规则不适合当前的需求,就要借助java.util.Comparator。
大小顺序和不能重复依据的是什么?
大小顺序和Comparable的compareTo或Comparator的compare方法有关;
不可重复也是和Comparable的compareTo或Comparator的compare方法有关。
Comparable的compareTo或Comparator的compare方法返回0,就认为是“重复”元素。

    TreeSet底层用红黑树(是一种自平衡的二叉树)实现。
3、LinkedHashSet
    元素也不能重复。
    元素有顺序,遍历结果是按照添加的顺序遍历的。
    底层实现:哈希表 + 链表。 链表的作用是记录添加的顺序。

    什么时候用LinkedHashSet?
    当我们需要元素不可重复,但是又要记录添加顺序时用它。

    LinkedHashSet比HashSet效率低,因为它在添加和删除等过程中,要考虑顺序的问题。

五、List集合

1、java.util.List接口:结合列表,也称为序列。

有序的collection(也称为序列)

此接口的用户可以队列表中每个元素的插入位置进行精确地控制。用户根据元素的整数索引(在列表中的位置)访问元素,并搜索列表中的元素。 == >List的很多操作和【index】有关与set不用,列表通常允许重复的元素。

List:有序可重复。

2、List的方法,在Collection的基础上增加了很多方法都和index有关

(1)添加

void add(int index, E element):在[index]位置添加一个元素

boolean addAll(int index,Collection<? extends E> c)在[index]位置添加多个元素

(2)删除

E remove(int index)

(3)修改

E set(int index , E element);替换[index]位置的元素

(4)查询

E get(int index); 获取[index]位置的元素

int indexOf(Object o ):查询o对象在当前List中的下标,如果没有返回-1,如果多个返回第一个

int lastIndexOf(Object o):查询o对象在当前List中的小标,如果没有返回-1,如果有多个,返回最后一个

List subList(int fromIndex, int toIndex):截取一段列表

(5)遍历

ListIterator listIerator()

ListItertor listIterator(int index)

ListIterator是Itertor的子接口比Itertor多了几个功能:

倒序遍历、获取下表信息、遍历过程中添加和替换元素

public class TestList {
    @Test
    public void test13() {
        List<String> list = new ArrayList<>();
        list.add("hello");
        list.add("java");
        list.add("world");
        list.add("xxx");

        ListIterator<String> iter = list.listIterator();
        while(iter.hasNext()){
            String element = iter.next();
            if(element.startsWith("w")){
                iter.set("atguigu");
            }
        }
        System.out.println(list);//[hello, java, atguigu, xxx]
    }


    @Test
    public void test12() {
        List<String> list = new ArrayList<>();
        list.add("hello");
        list.add("java");
        list.add("world");
        list.add("xxx");

        ListIterator<String> iter = list.listIterator();
        while(iter.hasNext()){
            String element = iter.next();
            if("world".equals(element)){
                iter.add("atguigu");//遍历过程中插入元素
            }
        }
        System.out.println(list);//[hello, java, world, atguigu, xxx]
    }

    @Test
    public void test11() {
        List<String> list = new ArrayList<>();
        list.add("hello");
        list.add("java");
        list.add("world");
        list.add("xxx");

        //倒序遍历
        ListIterator<String> iter = list.listIterator(list.size());//游标默认在列表的结尾
        while(iter.hasPrevious()){
            System.out.println(iter.previousIndex() + ":" + iter.previous());
        }
    }


    @Test
    public void test10() {
        List<String> list = new ArrayList<>();
        list.add("hello");
        list.add("java");
        list.add("world");
        list.add("xxx");

        ListIterator<String> iter = list.listIterator();//游标默认在列表的开头
        while(iter.hasNext()){
            //遍历过程中获取元素的下标
            System.out.println(iter.nextIndex() + ":" + iter.next());
        }
    }

    @Test
    public void test09() {
        List<String> list = new ArrayList<>();
        list.add("hello");
        list.add("java");
        list.add("world");
        list.add("xxx");

        ListIterator<String> iter = list.listIterator();
        while(iter.hasNext()){
            System.out.println(iter.next());
        }
    }

    @Test
    public void test08() {
        List<String> list = new ArrayList<>();
        list.add("hello");
        list.add("java");
        list.add("world");
        list.add("xxx");

        List<String> subList = list.subList(1, 3);
        System.out.println(subList);//[java, world]

    }

    @Test
    public void test07() {
        List<String> list = new ArrayList<>();
        list.add("hello");
        list.add("java");

        list.set(0,"atguigu");
        System.out.println(list);//[atguigu, java]
    }
    @Test
    public void test06() {
        List<Integer> list = new ArrayList<>();
        list.add(1);
        list.add(2);
        list.add(3);
        list.add(4);
        list.add(5);

        //删除元素值是1
//        list.remove((Object)1);

//        Collection<Integer> coll = list;
//        coll.remove(1);

        list.remove(Integer.valueOf(1));
        System.out.println(list);//[2, 3, 4, 5]
    }

    @Test
    public void test05() {
        List<Integer> list = new ArrayList<>();
        list.add(1);
        list.add(2);
        list.add(3);
        list.add(4);
        list.add(5);

        list.remove(1);//删除[1]位置的元素
        System.out.println(list);//[1, 3, 4, 5]
    }


    @Test
    public void test04() {
        List<Integer> list = new ArrayList<>();
        list.add(10);
        list.add(20);
        list.add(30);
        list.add(40);
        list.add(50);

        list.remove(1);
        System.out.println(list);//[10, 30, 40, 50]
    }

    @Test
    public void test03() {
        List<String> list = new ArrayList<>();
        list.add("hello");
        list.add("java");

        list.remove(0);
        System.out.println(list);
    }

    @Test
    public void test02() {
        List<String> list = new ArrayList<>();
        list.add("hello");
        list.add("java");


        List<String> list2 = new ArrayList<>();
        list2.add("chai");
        list2.add("xxx");

        list.addAll(1, list2);
        System.out.println(list);//[hello, chai, xxx, java]
    }

    @Test
    public void test01(){
        List<String> list = new ArrayList<>();

        list.add("hello");
        list.add("java");

        list.add(0,"atguigu");//[atguigu, hello, java]
        list.add(5,"chai");//IndexOutOfBoundsException: Index: 5, Size: 3
                                        //我们之前见过的ArrayIndexOutOfBoundsException是IndexOutOfBoundsException的子类

        System.out.println(list);
    }
}

3、List接口的实现类们

(1)ArrayList:动态数组

底层实现:数组

(2)Vector:动态数组

底层实现:数组
(3)LinkdList:双向链表

(4)Stack: 栈

底层实现:数组,因为他是Vector的子类

问题1:ArrayList与Vector的区别?
Vector是最古老的动态数组,比Collection和List接口还要早。线程安全。
Vector在扩容时,默认扩容为2倍。(扩容的频率第,浪费空间的可能性增加)
Vector的默认初始化容量是10。
ArrayList是比Vector新一点。线程不安全。
ArrayList在扩容时,默认扩容为1.5倍。(扩容的频率高,空间利用率高)
ArrayList的默认初始化容量,在JDK1.7之前也是10,在JDK1.7之后,一开始是0,第一次添加元素时初始化为10.

    无论是Vector还是ArrayList使用时,如果对元素的个数有预估,最好使用:
      ArrayList(int initialCapacity):一开始就指定初始化容量,就避免扩容。
      Vector(int initialCapacity) :一开始就指定初始化容量,就避免扩容。
      Vector(int initialCapacity, int capacityIncrement):除了指定初始化容量,还可以指定每次增加几个元素。

问题2:ArrayList从JDK1.7之后为什么默认初始化容量是0?
我们很多方法的返回值类型是List或ArrayList,特别是DAO(数据库层)方法。
这些方法在实现时,如果没有结果返回,一般不返回null。因为返回null,调用者就必须加if(xx != null)避免空指针,太麻烦。
不返回null,但是又没有数据,就返回了一个空的ArrayList对象。
那么如果初始化容量是10的话,其实这10个空间是浪费的。所以,就初始化容量为0。

问题3:动态数组与双向链表有什么区别?
区别一:
动态数组的元素:数据本身
双向链表的元素:结点

结点包含:
①前一个结点的首地址:prev
②数据本身
③后一个结点的首地址:next

区别二:
动态数组的元素是连续存储,提前开辟一整块连续的内存空间。
双向链表的元素不要求连续存储,来一个申请一个连接一个。

连续的好处:查询快,可以根据数组的首地址,快速的定位到某个元素。
不连续的缺点:只能从头或尾挨个查找,定位到对应的位置。

连续的坏处:插入,删除时,需要移动元素,插入时可能考虑扩容。
不连续的好处:不用提前申请空间,插入和删除只需要修改前后元素的prev和next

问题4:Stack
它是Vector的子类。
在Vector的基础上增加了几个方法:
①E peek():查看栈顶元素
②E pop():返回栈顶元素,栈顶换元素
③ E push(E item):把元素压入栈称为新的栈顶元素

栈的结构特点:后进先出(LIFO)或先进后出(FILO)
    LIFO:Last In First Out
    FILO:First In Last Out
    像桶,箱子

|       |  栈顶
|       |
|       |
|______ |  栈底

现在不太建议我们用Stack,如果要用栈结构,建议用LinkedList。因为LinkedList也提供了这些方法,使我们实现栈的效果。

问题5:LinkedList说起来是双向链表,但是它又是Queue,又是Deque,还可以用作栈等。
名称 deque 是“double ended queue(双端队列)”的缩写,通常读为“deck”。

    队列结构特点:先进先出(FIFO)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Java集合和Go的切片都是用来存储多个元素的数据结构,但是它们之间有一些区别。 Java集合是一种面向对象的数据结构,它可以存储任意类型的对象。Java提供了很多不同的集合类,比如List、Set和Map等,它们都是Java集合框架的一部分。 Go的切片是一种基于数组的动态数据结构,它可以存储任意类型的值,但是它只能存储值类型的元素,不能存储对象。Go的切片是通过数组的一个连续片段来实现的,它可以动态增长和缩小,而且它还提供了很多方便的操作函数。 总的来说,Java集合是一种面向对象的数据结构,它可以存储任意类型的对象,而Go的切片是一种基于数组的动态数据结构,它可以存储任意类型的值,但是只能存储值类型的元素。 ### 回答2: Java集合和Go的切片在一些方面有相似之处,也有很多不同之处。 首先,Java集合是由Java类提供的一组数据结构,如List、Set和Map等。这些集合类提供了丰富的功能和方法,例如添加、删除、查找和排序等。集合类在内部使用数组或链表等数据结构来存储元素,具有动态大小调整的能力。 Go的切片(Slice)是一种动态数组,可以根据需要自动扩容。与Java集合不同,Go的切片是一种原生类型,而不是基于类的数据结构。切片可以看作是数组的一部分,但是与传统的数组相比,它具有更灵活的特性和操作。 在使用上,Java集合类通常需要实例化一个具体的集合对象,然后通过方法来操作数据。而Go的切片则更加简洁,可以直接通过切片字面值来创建和操作切片,例如切片的初始化、添加元素、删除元素、切割切片等。 此外,Java集合类对元素的类型没有限制,可以存储不同类型的数据,但Go的切片只能存储相同类型的元素。 另一个区别是,Java集合类在进行元素访问时,可以使用迭代器、循环或索引等方式,而Go的切片使用索引来访问元素。 总的来说,Java集合和Go的切片在语法和特性上有所不同,满足了不同编程语言的需求和设计理念。 ### 回答3: Java中的集合(Collection)是用来存储一组对象的容器,而Go中的切片(Slice)则是一种动态数组的抽象。它们之间有以下几个区别: 1. 类型:Java中的集合泛型的,可以存储不同类型的对象。而Go中的切片是同一类型的元素组成的。 2. 大小调整:Java集合的大小可以自动调整,根据需要进行动态扩容或缩小。而Go的切片在创建时需要指定切片的长度,但它可以通过append()函数自动扩容,但不支持缩小。 3. 内存管理:Java集合由JVM进行垃圾回收,开发者无需自行管理内存。而Go语言中,切片会自动进行内存管理,具体的内存使用和释放由Go的垃圾回收器负责。 4. 访问方式:Java中的集合通过迭代器(Iterator)或者增强for循环进行遍历。而Go的切片可以使用索引进行访问,也可以使用for range语句进行遍历。 5. 功能:Java集合类别繁多,提供了诸如List、Set、Map等丰富的功能和数据结构。而Go的语言中只有切片和映射(Map)两种主要的数据结构。 总的来说,Java集合是一种通用的容器,提供了丰富的功能和灵活性,适用于各种场景。而Go的切片是一种动态数组,简单易用,适合快速的元素访问和操作。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值