java中什么是集合,如何使用集合,集合底层原理,ArrayList,LinkedList ,TreeMap,红黑树,HashMap2021-09-15java学习日记

1、集合概述

1.1、什么是集合?有什么用?

数组其实就是一个集合。集合实际上就是一个容器。可以来容纳其它类型的数据。

集合为什么说在开发中使用较多?
集合是一个容器,是一个载体,可以一次容纳多个对象。
在实际开发中,假设连接数据库,数据库当中有10条记录,
那么假设把这10条记录查询出来,在java程序中会将10条
数据封装成10个java对象,然后将10个java对象放到某一个
集合当中,将集合传到前端,然后遍历集合,将一个数据一个
数据展现出来。

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

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

你往集合c1中放数据,可能是放到数组上了。
你往集合c2中放数据,可能是放到二叉树上了。

你使用不同的集合等同于使用了不同的数据结构。

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

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

1.4、集合在java JDK中哪个包下?
java.util.*;
所有的集合类和集合接口都在java.util包下。

1.5、为了让大家掌握集合这块的内容,最好能将集合的继承结构图背会!!!
集合整个这个体系是怎样的一个结构,你需要有印象。

1.6、在java中集合分为两大类:
一类是单个方式存储元素:
单个方式存储元素,这一类集合中超级父接口:java.util.Collection;

一类是以键值对儿的方式存储元素
以键值对的方式存储元素,这一类集合中超级父接口:java.util.Map;

2、总结重点:

第一个重点:把集合继承结构图背会。
在这里插入图片描述在这里插入图片描述

第二个重点:把Collection接口中常用方法测试几遍。
多态遍历Collection

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

/*
关于java.util.Collection接口中常用的方法。
    1、Collection中能存放什么元素?
        没有使用“泛型”之前,Collection中可以存储Object的所有子类型。
        使用了“泛型”之后,Collection中只能存储某个具体的类型。
        集合后期我们会学习“泛型”语法。目前先不用管。Collection中什么都能存,
        只要是Object的子类型就行。(集合中不能直接存储基本数据类型,也不能存
        java对象,只是存储java对象的内存地址。)
    2、Collection中的常用方法
        boolean add(Object e) 向集合中添加元素
        int size()  获取集合中元素的个数
        void clear() 清空集合
        boolean contains(Object o) 判断当前集合中是否包含元素o,包含返回true,不包含返回false
        boolean remove(Object o) 删除集合中的某个元素。
        boolean isEmpty()  判断该集合中元素的个数是否为0
        Object[] toArray()  调用这个方法可以把集合转换成数组。【作为了解,使用不多。】
 */
public class CollectionTest01 {
    public static void main(String[] args) {
        // 创建一个集合对象
        //Collection c = new Collection(); // 接口是抽象的,无法实例化。
        // 多态
        Collection c = new ArrayList();
        // 测试Collection接口中的常用方法
        c.add(1200); // 自动装箱(java5的新特性。),实际上是放进去了一个对象的内存地址。Integer x = new Integer(1200);
        c.add(3.14); // 自动装箱
        c.add(new Object());
        c.add(new Student());
        c.add(true); // 自动装箱

        // 获取集合中元素的个数
        System.out.println("集合中元素个数是:" + c.size()); // 5

        // 清空集合
        c.clear();
        System.out.println("集合中元素个数是:" + c.size()); // 0

        // 再向集合中添加元素
        c.add("hello"); // "hello"对象的内存地址放到了集合当中。
        c.add("world");
        c.add("浩克");
        c.add("绿巨人");
        c.add(1);

        // 判断集合中是否包含"绿巨人"
        boolean flag = c.contains("绿巨人");
        System.out.println(flag); // true
        boolean flag2 = c.contains("绿巨人2");
        System.out.println(flag2); // false
        System.out.println(c.contains(1)); // true

        System.out.println("集合中元素个数是:" + c.size()); // 5

        // 删除集合中某个元素
        c.remove(1);
        System.out.println("集合中元素个数是:" + c.size()); // 4

        // 判断集合是否为空(集合中是否存在元素)
        System.out.println(c.isEmpty()); // false
        // 清空
        c.clear();
        System.out.println(c.isEmpty()); // true(true表示集合中没有元素了!)

        c.add("abc");
        c.add("def");
        c.add(100);
        c.add("helloworld!");
        c.add(new Student());

        // 转换成数组(了解,使用不多。)
        Object[] objs = c.toArray();
        for(int i = 0; i < objs.length; i++){
            // 遍历数组
            Object o = objs[i];
            System.out.println(o);
        }
    }
}

class Student{

}

深入Collection中的contains方法

注意:remove方法同理,底层调用了equals方法

import java.util.ArrayList;
import java.util.Collection;
 
/*
深入Collection集合的contains方法:
    boolean contains(Object o)
        判断集合中是否包含某个对象o
        如果包含返回true, 如果不包含返回false。

    contains方法是用来判断集合中是否包含某个元素的方法,
    那么它在底层是怎么判断集合中是否包含某个元素的呢?
        调用了equals方法进行比对。
        equals方法返回true,就表示包含这个元素。
 */
public class CollectionTest04 {
    public static void main(String[] args) {
        // 创建集合对象
        Collection c = new ArrayList();

        // 向集合中存储元素
        String s1 = new String("abc"); // s1 = 0x1111
        c.add(s1); // 放进去了一个"abc"

        String s2 = new String("def"); // s2 = 0x2222
        c.add(s2);

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

        // 新建的对象String
        String x = new String("abc"); // x = 0x5555
        // c集合中是否包含x?结果猜测一下是true还是false?
        System.out.println(c.contains(x)); //判断集合中是否存在"abc" true
    }
}

第三个重点:把迭代器弄明白。

迭代器使用的过程中,原集合不可改动,否则会出现异常

迭代器遍历ArrayList

/**
 * 关于集合遍历/迭代专题。(重点:五颗星*****)
 */
public class CollectionTest02 {
    public static void main(String[] args) {
        // 注意:以下讲解的遍历方式/迭代方式,是所有Collection通用的一种方式。
        // 在Map集合中不能用。在所有的Collection以及子类中使用。
        // 创建集合对象
        Collection c = new ArrayList(); // 后面的集合无所谓,主要是看前面的Collection接口,怎么遍历/迭代。
        // 添加元素
        c.add("abc");
        c.add("def");
        c.add(100);
        c.add(new Object());
        // 对集合Collection进行遍历/迭代
        // 第一步:获取集合对象的迭代器对象Iterator
        Iterator it = c.iterator();
        // 第二步:通过以上获取的迭代器对象开始迭代/遍历集合。
        /*
            以下两个方法是迭代器对象Iterator中的方法:
                boolean hasNext()如果仍有元素可以迭代,则返回 true。
                Object next() 返回迭代的下一个元素。
         */
        while(it.hasNext()){
            Object obj = it.next();
            System.out.println(obj);
        }

        // 一直取,不判断,会出现异常:java.util.NoSuchElementException
        /*while(true){
            Object obj = it.next();
            System.out.println(obj);
        }*/

        /*boolean hasNext = it.hasNext();
        System.out.println(hasNext);
        if(hasNext) {
            // 不管你当初存进去什么,取出来统一都是Object。
            Object obj = it.next();
            System.out.println(obj);
        }

        hasNext = it.hasNext();
        System.out.println(hasNext);
        if(hasNext) {
            Object obj = it.next();
            System.out.println(obj);
        }

        hasNext = it.hasNext();
        System.out.println(hasNext);
        if(hasNext) {
            Object obj = it.next();
            System.out.println(obj);
        }

        hasNext = it.hasNext();
        System.out.println(hasNext);
        if(hasNext) {
            Object obj = it.next();
            System.out.println(obj);
        }

        hasNext = it.hasNext();
        System.out.println(hasNext);
        if(hasNext) {
            Object obj = it.next();
            System.out.println(obj);
        }*/
    }
}

迭代器遍历HashSet 和ArrayList


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

/*
关于集合的迭代/遍历
 */
public class CollectionTest03 {
    public static void main(String[] args) {
        // 创建集合对象
        Collection c1  = new ArrayList(); // ArrayList集合:有序可重复
        // 添加元素
        c1.add(1);
        c1.add(2);
        c1.add(3);
        c1.add(4);
        c1.add(1);

        // 迭代集合
        Iterator it = c1.iterator();
        while(it.hasNext()){
            // 存进去是什么类型,取出来还是什么类型。
            Object obj = it.next();
            /*if(obj instanceof Integer){
                System.out.println("Integer类型");
            }*/
            // 只不过在输出的时候会转换成字符串。因为这里println会调用toString()方法。
            System.out.println(obj);
        }

        // HashSet集合:无序不可重复
        Collection c2 = new HashSet();
        // 无序:存进去和取出的顺序不一定相同。
        // 不可重复:存储100,不能再存储100.
        c2.add(100);
        c2.add(200);
        c2.add(300);
        c2.add(90);
        c2.add(400);
        c2.add(50);
        c2.add(60);
        c2.add(100);
        Iterator it2 = c2.iterator();
        while(it2.hasNext()){
            System.out.println(it2.next());
        }
    }
}

第四个重点:Collection接口中的remove方法和contains方法底层都会调用equals,
这个弄明白。

1、List接口中的常用方法。
List是Collection接口的子接口。所以List接口中有一些特有的方法。
void add(int index, Object element)
Object set(int index, Object element)
Object get(int index)
int indexOf(Object o)
int lastIndexOf(Object o)
Object remove(int index)
2、迭代器迭代元素的过程中不能使用集合对象的remove方法删除元素,
要使用迭代器Iterator的remove方法来删除元素,防止出现异常:
ConcurrentModificationException

3、ArrayList
ArrayList集合初始化容量10
扩容为原容量1.5倍。
按位运算 old长度+old按右移动一位,所以是1.5倍扩容
底层是数组。

数组优点和缺点要能够说出来!
ArrayList 随机增删元素效率低,查询效率高!
另外要注意:ArrayList集合末尾增删元素效率还是可以的。

4、链表数据结构
第一:单向链表和双向链表数据结构要理解。
第二:链表数据结构的优点和缺点要能够说出来。
随机增删元素效率高!查询效率低1

5、Vector:
Vector初始化容量是10.
扩容为原容量的2倍。
底层是数组。
Vector底层是线程安全的。

怎么得到一个线程安全的List:
Collections.synchronizedList(list);

6、JDK5.0新特性:泛型
第一:集合使用泛型来减少向下转型的操作。
第二:怎么使用泛型?
第三:怎么自定义泛型?

7、JDK5.0新特性:
foreach
对数组怎么遍历?
for(int i : arr){
System.out.println(i);
}
对集合怎么遍历?
for(String s : list){
System.out.println(s);
}

8、JDK8新特性:钻石表达式
List list = new ArrayList<>();
类型自动推断!

1、掌握Map接口中常用方法。
put 方法

import java.util.HashSet;
import java.util.Set;

/*
1、向Map集合中存,以及从Map集合中取,都是先调用key的hashCode方法,然后再调用equals方法!
equals方法有可能调用,也有可能不调用。
    拿put(k,v)举例,什么时候equals不会调用?
        k.hashCode()方法返回哈希值,
        哈希值经过哈希算法转换成数组下标。
        数组下标位置上如果是null,equals不需要执行。
    拿get(k)举例,什么时候equals不会调用?
        k.hashCode()方法返回哈希值,
        哈希值经过哈希算法转换成数组下标。
        数组下标位置上如果是null,equals不需要执行。

2、注意:如果一个类的equals方法重写了,那么hashCode()方法必须重写。
并且equals方法返回如果是true,hashCode()方法返回的值必须一样。
    equals方法返回true表示两个对象相同,在同一个单向链表上比较。
    那么对于同一个单向链表上的节点来说,他们的哈希值都是相同的。
    所以hashCode()方法的返回值也应该相同。

3、hashCode()方法和equals()方法不用研究了,直接使用IDEA工具生成,但是这两个方法需要同时生成。

4、终极结论:
    放在HashMap集合key部分的,以及放在HashSet集合中的元素,需要同时重写hashCode方法和equals方法。

5、对于哈希表数据结构来说:
    如果o1和o2的hash值相同,一定是放到同一个单向链表上。
    当然如果o1和o2的hash值不同,但由于哈希算法执行结束之后转换的数组下标可能相同,此时会发生“哈希碰撞”。

 */
public class HashMapTest02 {
    public static void main(String[] args) {

        Student s1 = new Student("zhangsan");
        Student s2 = new Student("zhangsan");

        // 重写equals方法之前是false
        //System.out.println(s1.equals(s2)); // false

        // 重写equals方法之后是true
        System.out.println(s1.equals(s2)); //true (s1和s2表示相等)

        System.out.println("s1的hashCode=" + s1.hashCode()); //284720968 (重写hashCode之后-1432604525)
        System.out.println("s2的hashCode=" + s2.hashCode()); //122883338 (重写hashCode之后-1432604525)

        // s1.equals(s2)结果已经是true了,表示s1和s2是一样的,相同的,那么往HashSet集合中放的话,
        // 按说只能放进去1个。(HashSet集合特点:无序不可重复)
        Set<Student> students = new HashSet<>();
        students.add(s1);
        students.add(s2);
        System.out.println(students.size()); // 这个结果按说应该是1. 但是结果是2.显然不符合HashSet集合存储特点。怎么办?
    }
}

size方法
get方法
map.entrySet()方法
在这里插入图片描述

2、遍历Map集合的两种方式都要精通。
第一种:获取所有key,遍历每个key,通过key获取value.
第二种:获取Set<Map.Entry>即可,遍历Set集合中的Entry
调用entry.getKey() entry.getValue()

3、了解哈希表数据结构。

4、存放在HashMap集合key部分和HashSet集合中的元素需要同时重写hashCode和equals。

5、HashMap和Hashtable的区别。
HashMap:
初始化容量16,扩容2倍。
非线程安全
key和value可以为null。

Hashtable
初始化容量11,扩容2倍+1
线程安全
key和value都不能是null。

6、Properties类的常用两个方法。
setProperty
getProperty

7、了解自平衡二叉树数据结构。
左小右大原则存储。
中序遍历方式。
在这里插入图片描述

8、TreeMap的key或者TreeSet集合中的元素要想排序,有两种实现方式:
第一种:实现java.lang.Comparable接口。
第二种:单独编写一个比较器Comparator接口。

9、集合工具类Collections:
synchronizedList方法
sort方法(要求集合中元素实现Comparable接口。)
1、集合这块最主要掌握什么内容?
1.1、每个集合对象的创建(new)
1.2、向集合中添加元素
1.3、从集合中取出某个元素
1.4、遍历集合
1.5、主要的集合类:
ArrayList


/*
    1.1、每个集合对象的创建(new)
	1.2、向集合中添加元素
	1.3、从集合中取出某个元素
	1.4、遍历集合
 */
public class ArrayListTest {
    public static void main(String[] args) {
        // 创建集合对象
        //ArrayList<String> list = new ArrayList<>();
        LinkedList<String> list = new LinkedList<>();
        // 添加元素
        list.add("zhangsan");
        list.add("lisi");
        list.add("wangwu");
        // 从集合中取出某个元素
        // List集合有下标
        String firstElt = list.get(0);
        System.out.println(firstElt);
        // 遍历(下标方式)
        for(int i = 0; i < list.size(); i++){
            String elt = list.get(i);
            System.out.println(elt);
        }
        // 遍历(迭代器方式,这个是通用的,所有Collection都能用)
        Iterator<String> it = list.iterator();
        while(it.hasNext()){
            System.out.println(it.next());
        }

        // while循环修改为for循环
        /*for(Iterator<String> it2 = list.iterator(); it2.hasNext(); ){
            System.out.println("====>" + it2.next());
        }*/

        // 遍历(foreach方式)
        for(String s : list){
            System.out.println(s);
        }
    }
}

LinkedList

HashSet (HashMap的key,存储在HashMap集合key的元素需要同时重写hashCode + equals)


/*
    1.1、每个集合对象的创建(new)
	1.2、向集合中添加元素
	1.3、从集合中取出某个元素
	1.4、遍历集合
	1.5、测试HashSet集合的特点:无序不可重复。
 */
public class HashSetTest {
    public static void main(String[] args) {
        // 创建集合对象
        HashSet<String> set = new HashSet<>();

        // 添加元素
        set.add("abc");
        set.add("def");
        set.add("king");

        // set集合中的元素不能通过下标取了。没有下标
        // 遍历集合(迭代器)
        Iterator<String> it = set.iterator();
        while(it.hasNext()){
            System.out.println(it.next());
        }

        // 遍历集合(foreach)
        for(String s : set){
            System.out.println(s);
        }

        set.add("king");
        set.add("king");
        set.add("king");
        System.out.println(set.size()); //3 (后面3个king都没有加进去。)

        set.add("1");
        set.add("10");
        set.add("2");

        for(String s : set){
            System.out.println("--->" + s);
        }

        // 创建Set集合,存储Student数据
        Set<Student> students = new HashSet<>();

        Student s1 = new Student(111, "zhangsan");
        Student s2 = new Student(222, "lisi");
        Student s3 = new Student(111, "zhangsan");

        students.add(s1);
        students.add(s2);
        students.add(s3);

        System.out.println(students.size()); // 2

        // 遍历
        for(Student stu : students){
            System.out.println(stu);
        }

    }
}

class Student {
    int no;
    String name;

    public Student() {
    }

    public Student(int no, String name) {
        this.no = no;
        this.name = name;
    }

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

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Student student = (Student) o;
        return no == student.no &&
                Objects.equals(name, student.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(no, name);
    }
}

TreeSet


/*
    1.1、每个集合对象的创建(new)
	1.2、向集合中添加元素
	1.3、从集合中取出某个元素
	1.4、遍历集合
	1.5、测试TreeSet集合中的元素是可排序的。
	1.6、测试TreeSet集合中存储的类型是自定义的。
	1.7、测试实现Comparable接口的方式
	1.8、测试实现Comparator接口的方式(最好测试以下匿名内部类的方式)
 */
public class TreeSetTest {
    public static void main(String[] args) {
        // 集合的创建(可以测试以下TreeSet集合中存储String、Integer的。这些类都是SUN写好的。)
        //TreeSet<Integer> ts = new TreeSet<>();

        // 编写比较器可以改变规则。
        TreeSet<Integer> ts = new TreeSet<>(new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return o2 - o1; // 自动拆箱
            }
        });

        // 添加元素
        ts.add(1);
        ts.add(100);
        ts.add(10);
        ts.add(10);
        ts.add(10);
        ts.add(10);
        ts.add(0);

        // 遍历(迭代器方式)
        Iterator<Integer> it = ts.iterator();
        while(it.hasNext()) {
            Integer i = it.next();
            System.out.println(i);
        }

        // 遍历(foreach)
        for(Integer x : ts){
            System.out.println(x);
        }

        // TreeSet集合中存储自定义类型
        TreeSet<A> atree = new TreeSet<>();

        atree.add(new A(100));
        atree.add(new A(200));
        atree.add(new A(500));
        atree.add(new A(300));
        atree.add(new A(400));
        atree.add(new A(1000));

        // 遍历
        for(A a : atree){
            System.out.println(a);
        }

        //TreeSet<B> btree = new TreeSet<>(new BComparator());
        // 匿名内部类方式。
        TreeSet<B> btree = new TreeSet<>(new Comparator<B>() {
            @Override
            public int compare(B o1, B o2) {
                return o1.i - o2.i;
            }
        });

        btree.add(new B(500));
        btree.add(new B(100));
        btree.add(new B(200));
        btree.add(new B(600));
        btree.add(new B(300));
        btree.add(new B(50));

        for(B b : btree){
            System.out.println(b);
        }
    }
}

// 第一种方式:实现Comparable接口
class A implements Comparable<A>{
    int i;

    public A(int i){
        this.i = i;
    }

    @Override
    public String toString() {
        return "A{" +
                "i=" + i +
                '}';
    }

    @Override
    public int compareTo(A o) {
        //return this.i - o.i;
        return o.i - this.i;
    }
}

class B {
    int i;
    public B(int i){
        this.i = i;
    }

    @Override
    public String toString() {
        return "B{" +
                "i=" + i +
                '}';
    }
}

// 比较器
class BComparator implements Comparator<B> {

    @Override
    public int compare(B o1, B o2) {
        return o1.i - o2.i;
    }
}

HashMap


/*
    1.1、每个集合对象的创建(new)
	1.2、向集合中添加元素
	1.3、从集合中取出某个元素
	1.4、遍历集合
 */
public class HashMapTest {
    public static void main(String[] args) {
        // 创建Map集合
        Map<Integer, String> map = new HashMap<>();
        // 添加元素
        map.put(1, "zhangsan");
        map.put(9, "lisi");
        map.put(10, "wangwu");
        map.put(2, "king");
        map.put(2, "simth"); // key重复value会覆盖。
        // 获取元素个数
        System.out.println(map.size());
        // 取key是2的元素
        System.out.println(map.get(2)); // smith
        // 遍历Map集合很重要,几种方式都要会。
        // 第一种方式:先获取所有的key,遍历key的时候,通过key获取value
        Set<Integer> keys = map.keySet();
        for(Integer key : keys){
            System.out.println(key + "=" + map.get(key));
        }

        // 第二种方式:是将Map集合转换成Set集合,Set集合中每一个元素是Node
        // 这个Node节点中有key和value
        Set<Map.Entry<Integer,String>> nodes = map.entrySet();
        for(Map.Entry<Integer,String> node : nodes){
            System.out.println(node.getKey() + "=" + node.getValue());
        }
    }
}

Properties
TreeMap

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值