Java笔记02

集合

  • 可以动态保存任意多个对象,方便使用
  • 提供了一系列方便的操作对象的方法:add、remove、set、get等
  • 使用集合添加、删除新元素的代码更简洁
    在这里插入图片描述
    集合主要是两组(单列集合、双列集合)
  • Collection接口有两个重要的子接口List Set,他们的实现子类都是单列集合(在集合里面都是单个单个的元素)
  • Map接口的实现子类是双列集合,存放的是Key-Value
package collection_;

import java.util.ArrayList;
import java.util.HashMap;

public class Collection_ {
    public static void main(String[] args) {
        ArrayList arrayList = new ArrayList();
        // 单列
        arrayList.add("jack");
        arrayList.add("tom");

        HashMap hashMap = new HashMap();
        // 双列 k-v
        hashMap.put("No1", "北京");
        hashMap.put("No2", "上海");
    }
}

集合常用方法

package collection_;

import java.util.ArrayList;
import java.util.List;

public class CollectionMethod {
    public static void main(String[] args) {
        // ArrayList实现了List接口,创建一个ArrayList对象,使用它的接口来接收
        List list = new ArrayList();
        // add:添加单个元素
        list.add("jack");
        list.add(10);
        list.add(true);
        // 注意list里面是对象
        // list.add(10)执行的其实是list.add(new Integer(10))
        System.out.println("list = " + list);       // list = [jack, 10, true]

        // remove删除指定元素 可以指定索引或者指定对象
        list.remove(0);
        list.remove("jack");

        // contains查找元素是否存在
        System.out.println(list.contains("jack"));

        // size获取元素的个数
        System.out.println(list.size());

        //isEmpty判断是否为空
        System.out.println(list.isEmpty());

        // clear 清理
        list.clear();

        // addAll 添加多个元素
        ArrayList list2 = new ArrayList();
        list2.add("mango");
        list2.add("kaka");
        list.addAll(list2); // 可以在指定位置加入集合,也可以不指定
        System.out.println("list = " + list);

        // containsAll:查找多个元素是否都存在 传入的是集合
        System.out.println(list.containsAll(list2));

        // removeAll:删除多个元素 传入要删除元素的集合
        list.removeAll(list2);
    }
}

Collection接口遍历元素

方式1——使用Iterator(迭代器)
  • Iterator对象称为迭代器,主要用于遍历Collection集合中的元素
  • 所有实现了Collection接口的集合类都有一个iterator()方法,用以返回一个实现了Iterator接口的对象,即可以返回一个迭代器
  • Iterator仅用于遍历集合,Iterator本身并不存放对象

注意:在调用iterator.next()方法之前必须要调用iterator.hasNext()进行检测。若不调用,且下一条记录无效,直接调用iterator.next()会抛出NoSuchElementException异常

package collection_;

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

public class CollectionIterator {
    public static void main(String[] args) {
        Collection col = new ArrayList();
        col.add(new Book("三国演义", "罗贯中", 10.1));
        col.add(new Book("小李飞刀", "古龙", 5.1));
        col.add(new Book("红楼梦", "曹雪芹", 34.6));
        // 进行遍历
        // 1. 先得到col对应的迭代器
        Iterator iterator = col.iterator();
        while (iterator.hasNext()){     // 判断是否还有数据
            // 返回下一个元素,类型是Object
            Object obj = iterator.next();
            System.out.println(obj);

        }

        // 当退出while循环后,这时iterator迭代器指向最后的元素
        // 如果再执行 iterator.next() 会抛出NoSuchElementException
        // 如果希望再次遍历,需要重置迭代器
        iterator = col.iterator();      //重置迭代器游标操作

    }
}

class Book{
    private String name;
    private String author;
    private double price;

    public Book(String name, String author, double price) {
        this.name = name;
        this.author = author;
        this.price = price;
    }

    public String getName() {
        return name;
    }

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

    public String getAuthor() {
        return author;
    }

    public void setAuthor(String author) {
        this.author = author;
    }

    public double getPrice() {
        return price;
    }

    public void setPrice(double price) {
        this.price = price;
    }

    @Override
    public String toString() {
        return "Book{" +
                "name='" + name + '\'' +
                ", author='" + author + '\'' +
                ", price=" + price +
                '}';
    }
}
方式2——for循环增强

增强for循环,可以代替iterator迭代器,特点:增强for就是简化的iterator,本质一样。只能用于遍历集合或数组

package collection_;

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

public class CollectionFor {
    public static void main(String[] args) {
        Collection col = new ArrayList();
        col.add(new Book("三国演义", "罗贯中", 10.1));
        col.add(new Book("小李飞刀", "古龙", 5.1));
        col.add(new Book("红楼梦", "曹雪芹", 34.6));


        // 使用增强for,在Collection集合
        // 增强for,底层仍然是迭代器
        for (Object book : col) {       // 把col里面的每一个元素给book,然后输出遍历
            System.out.println("book = " + book);
        }

        // 增强for,也可以直接在数组使用
    }
}

List

List接口基本介绍
package collection_.list;

import java.sql.Array;
import java.util.ArrayList;
import java.util.List;

public class List_ {
    public static void main(String[] args) {
        // 1. List集合类中元素有序(即添加顺序和取出顺序一致),且可以有重复元素
        List list = new ArrayList();
        list.add("jack");
        list.add("tom");
        list.add("marry");
        list.add("tom");        // 可重复
        System.out.println("list:" + list);     // list:[jack, tom, marry, tom]

        // 2. List集合中的每个元素都有其对应的顺序索引,即支持索引
        System.out.println(list.get(2));        // 索引从0开始

    }
}

List接口常用方法
  • add
  • addAll
  • get
  • indexOf
  • lastIndexOf
  • remove
  • set(设置index位置的元素为xx,相当于是替换)
  • subList(返回从fromIndex到toIndex位置的子集合 左闭右开)
package collection_.list;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

/**
 * 
 * 按价格排序,从低到高(使用冒泡法)
 */
public class ListExercise01 {
    public static void main(String[] args) {
        List list = new ArrayList();
        list.add(new Book("king", 23.5, "smith"));
        list.add(new Book("vot", 2.5, "lisa"));
        list.add(new Book("kak", 89.5, "marry"));

        // 遍历
        for (Object o :list) {
            System.out.println(o);;
        }

        // 冒泡排序
        sort(list);
        System.out.println("============排序后===============");
        for (Object o :list) {
            System.out.println(o);;
        }

    }


    public static void sort(List list) {
        int listSize = list.size();
        for (int i = 0; i < listSize - 1; i++) {
            for (int j = 0; j < listSize - 1 - i; j++) {
                // 取出对象Book
                Book book1 = (Book) list.get(j);       // list.get(j)得到的是Object,需要进行向下转型,用Book对象引用来接收,这样才能调用getPrice方法得到每本书的价格
                Book book2 = (Book) list.get(j + 1);
                if (book1.getPrice() > book2.getPrice()) {    // 交换
                    list.set(j, book2);
                    list.set(j + 1, book1);
                }
            }
        }
    }
}
class Book {
    private String name;
    private double price;
    private String author;

    public Book(String name, double price, String author) {
        this.name = name;
        this.price = price;
        this.author = author;
    }

    public String getName() {
        return name;
    }

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

    public double getPrice() {
        return price;
    }

    public void setPrice(double price) {
        this.price = price;
    }

    public String getAuthor() {
        return author;
    }

    public void setAuthor(String author) {
        this.author = author;
    }

    @Override
    public String toString() {
        return "name=" + name + '\t' +
                " price=" + price + '\t' +
                " author=" + author;
    }
}
Vector和ArrayList的比较
*底层结构版本线程安全(同步)效率扩容倍数
ArrayList可变数组jdk1.2不安全,效率高如果是有参构造,每次扩容为1.5倍;如果是无参,第一次是10,从第二次开始按1.5倍扩容
Vector可变数组jdk1.0安全,效率不高如果是无参,默认10,满后,就按2倍扩容; 如果指定大小,则每次按2倍扩容
LinkedList底层结构
  • LinkedList底层实现了双向链表和双端队列特点
  • 可以添加任意元素(元素可以重复),包括null
  • 线程不安全,没有实现同步

LinkedList的底层操作机制:
1)LinkedList底层维护了一个双向链表
2)LinkedList中维护了两个属性first和和last分别指向首节点和尾结点
3)每个节点(Node对象),里面又维护了prev、next、item三个属性,其中通过prev指向前一个,通过next指向后一个节点,最终实现双向链表
4)所以LinkedList的元素的添加和删除,不是通过数组完成的,相对来说效率较高

  • 链表相关基础操作
package collection_.list;

public class LinkedList01 {
    public static void main(String[] args) {
        Node jack = new Node("jack");
        Node tom = new Node("tom");
        Node mango = new Node("mango");

        // 连接三个节点,形成双向链表
        jack.next = tom;
        tom.next = mango;
        tom.pre = jack;
        mango.pre = tom;

        Node first = jack;  // 让first引用指向Jack
        Node last = mango;

        // 从头到尾遍历
        while (true){
            if (first == null){
                break;
            }
            // 输出first信息
            System.out.println(first);      // 自动调用toString方法
            first = first.next;
        }
        System.out.println("================");


        // 链表的添加删除操作
        Node smith = new Node("smith");
        smith.next = mango;
        smith.pre = tom;
        tom.next = smith;
        mango.pre = smith;

        // 从尾到头遍历
        while (true){
            if (last == null){
                break;
            }
            System.out.println(last);
            last = last.pre;
        }
    }
}


// 定义一个Node类, Node 对象  表示双向链表的一个节点
class Node{
    public Object item;     // 真正存放数据
    public Node next;
    public Node pre;

    public Node(Object name) {
        this.item = name;
    }

    @Override
    public String toString() {
        return "Node name = " + item;
    }
}
*底层结构增删的效率改查的效率
ArrayList可变数组较低, 数组扩容较高
LinkedList双向链表较高, 通过链表追加较低
  • 如果改查的操作比较多,选择ArrayList
  • 如果增删的操作比较多,选择LinkedList
  • 一般来说,在程序中,80%-90%都是查询,因此大部分情况下会选择ArrayList

Set

  • set接口
    1)无序(添加和取出的顺序不一致),没有索引
    2)不允许重复元素,所以最多包含一个null

  • Set接口的常用方法
    和List接口一样,Set接口也是Collection的子接口,因此常用方法和Collection接口一样

  • Set接口的遍历方式
    同Collection的遍历方式一样,因为Set接口是Collection接口的子接口
    1)可以使用迭代器
    2)增强for
    3)不能使用索引的方式来获取

package collection_.set_;

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

public class SetMethod {
    public static void main(String[] args) {
        // 以Set接口的实现类 HashSet 为例,说明Set接口的方法
        // set接口的实现类的对象(一般说接口对象,指的就是实现了这个接口的类的对象),不能存放重复的对象
        // set接口对象存放数据是无序的(添加和取出顺序不一致)
        Set set = new HashSet();
        set.add("john");
        set.add("lucy");
        set.add("john");
        set.add("jack");
        set.add(null);
        set.add(null);

        System.out.println(set);
        // 遍历
        // 方式1:使用迭代器
        Iterator iterator = set.iterator();
        while (iterator.hasNext()){
            Object obj = iterator.next();
            System.out.println("obj = " + obj);
        }

        System.out.println("============");

        // 方式2:增强for
        for (Object o : set) {
            System.out.println(o);
        }

    }
}

关于set的添加

package collection_.set_;

import java.util.HashSet;

public class HashSet01 {
    public static void main(String[] args) {
        HashSet set = new HashSet();

        /**
         * 1. 在执行add后,会返回一个boolean值,如果添加成功,返回true;否则返回false
         * 2. 可以通过remove指定删除哪个对象
         */
        System.out.println(set.add("john"));
        System.out.println(set.add("lucy"));
        System.out.println(set.add("john"));    // 此时返回false,因为前面已经添加过john了
        System.out.println(set.add("jack"));
        System.out.println(set.add("Rose"));

        set.remove("john");
        System.out.println("set = " + set);
        System.out.println("================");
        set = new HashSet();
        set.add("lucy");
        set.add("lucy");
        set.add(new Dog("tom"));
        set.add(new Dog("tom"));
        // 这两个Dog都能过add成功,虽然这两个名字一样,但是他们是不同的对象
        System.out.println("set = " + set);

        System.out.println("==============");
        set.add(new String("hsp"));
        set.add(new String("hsp"));     // 这里只加入了一个hsp
        System.out.println(set);
        System.out.println("=================");
        System.out.println(new String("mango").hashCode());
        System.out.println(new String("mango").hashCode());
    }
}


class Dog{
    private String name;

    public Dog(String name) {
        this.name = name;
    }

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

HashSet底层是HashMap,HashMap底层是数组+链表+红黑树

package collection_.set_;

public class HashSetStructure {
    public static void main(String[] args) {

        // HashSet底层是HashMap,HashMap底层是数组+链表+红黑树

        //  模拟一个HashMap的底层结构
        // 1. 创建一个数组,数组的类型是Node[]
        // 也有人直接把Node[] 数组称为表
        Node[] table = new Node[16];

        // 创建节点
        Node john = new Node("john", null);
        table[2] = john;

        Node jack = new Node("jack", null);
        john.next = jack;       // 将Jack挂载到John

        Node rose = new Node("Rose", null);
        jack.next = rose;

        Node lucy = new Node("lucy", null);
        table[3] = lucy;
        System.out.println("table = " + table);
    }
}

class Node{     // 结点,存储数据,可以指向下一个结点,从而形成链表
    Object item;
    Node next;

    public Node(Object item, Node next) {
        this.item = item;
        this.next = next;
    }
}

HashSet添加元素底层:

  • HashSet底层是HashMap
  • 添加一个元素时,先得到hash值 - 会转成- 索引值
  • 找到存储数据表table,看这个索引位置是否已经有存放的元素
  • 如果没有,直接加入
  • 如果有,调用equals比较,如果相同则放弃添加,如果不相同,则添加到最后
  • 在Java8中,如果一条链表的元素个数到达TREEIFY_THRESHOLD(默认是8),并且table的大小>=MIN_THEEIFY_CAPACITY(默认64),就会进行树化(红黑树)

HashSet源码解析

package collection_.set_;

import java.util.HashSet;

public class HashSetSource {
    public static void main(String[] args) {
        HashSet hashSet = new HashSet();
        hashSet.add("java");
        hashSet.add("php");
        hashSet.add("java");
        System.out.println("set = " + hashSet);

        /**
         * HashSet源码
         * 1. 执行HashSet() 构造器
         *      public HashSet() {
         *         map = new HashMap<>();
         *      }
         * 2. 执行add()
         *      public boolean add(E e) {   // e = "java"
         *         return map.put(e, PRESENT)==null;
         *      }
         *      // 其中,PRESENT的定义为:private static final Object PRESENT = new Object();
         * 3. 执行put() 该算法会执行 hash(key),得到key对应的hash值  算法 (h = key.hashCode()) ^ (h >>> 16) (也就是说计算得到的值并不是等于hashcode,进行了处理,避免冲突)
         *      public V put(K key, V value) {      // key = “java” value = PRESENT (静态共享的,所以在后面,变化的只有key)
         *         return putVal(hash(key), key, value, false, true);
         *      }
         * 4. 执行putVal
         *      final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
         *                    boolean evict) {
         *         Node<K,V>[] tab; Node<K,V> p; int n, i;      // 定义了辅助变量
         *         // table 就是HashMap的一个数组,类型是Node[]
         *         // if语句表示如果当前table是null,或者大小=0,就进行第一次扩容,到16个空间
         *         if ((tab = table) == null || (n = tab.length) == 0)
         *             n = (tab = resize()).length;
         *
         *        // (1)根据key,得到hash,去计算该key应该存放到table表的哪个索引位置
         *        // 并把tab中这个位置的对象赋给p
         *        // (2)判断p是否为null,如果p为null,表示还没有存放元素,就创建一个Node(key = "java", value = PRESENT),执行 tab[i] = newNode(hash, key, value, null)
         *              // 这里n是table容量,比如第一次是16,那么这里n就是16,计算key存放的位置,i = (n - 1) & hash,hash是key对应的hash,看tab中i的位置是不是已经存放了元素 ,因此当相同key存入的时候,i是一样的,因此后面相同的key执行的是else语句
         *              // 哈希函数:相同内容的hash值一定相同,不同内容的hash值可能相同,又叫哈希碰撞
         *         if ((p = tab[i = (n - 1) & hash]) == null)       // 注意&这里是位运算符,不是逻辑运算符
         *              // 注意,这里把key对应的hash也传入,是因为后面会进行比较,看他们的hash是不是一样的        这里的null表示的就是该节点后面没有节点
         *             tab[i] = newNode(hash, key, value, null);        // 注意,key是真正存放的值,value是PRESENT,不变的
         *         else {
         *              // 开发技巧:在需要局部变量(辅助变量)的时候再创建
         *             Node<K,V> e; K k;
         *             // 如果当前索引位置对应的链表的第一个元素和准备添加的key的hash值一样
         *             // 并且满足下面两个条件之一: 就不能加入
         *             // (1)准备加入的key和p指向的Node节点的key是同一对象   (2)p指向的Node节点的key的equals()和准备加入的key比较后相同(这个equals()方法不能简单的理解为判断内容一样,因为每个类都可以有自己的equals()方法,程序员可以自己定)
         *             (也就是不仅要判断hash是不是一样,还要判断元素值,目的是判断是相同元素还是哈希碰撞导致的hash一样)
         *             if (p.hash == hash &&
         *                 ((k = p.key) == key || (key != null && key.equals(k))))
         *                 e = p;       // 没有创建新节点加入,e指向已存在的
         *
         *             // 再判断p是不是一棵红黑树
         *             // 如果是一棵红黑树,就执行putTreeVal,来进行添加
         *             else if (p instanceof TreeNode)
         *                 e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
         *             else {   // 如果table对应索引位置,已经是一个链表,就使用for循环
         *                      // (1)依次和该链表的每一个元素比较后,都不相同,则加入到该链表的最后
         *                      //      注意在把元素添加到链表后 ,立即判断该链表是否已经达到8个节点(TREEIFY_THRESHOLD),如果到达8个,就调用treeifyBin() 对当前这个链表进行树化(转成红黑树)
         *                      //      注意,在转成红黑树时,要进行判断,判断条件如下:
         *                          if (tab == null || (n = tab.length) < MIN_TREEIFY_CAPACITY)  // MIN_TREEIFY_CAPACITY默认64
         *                               resize();
         *                      // 如果上面条件成立,就先对table表扩容
         *                      // 只要上面条件不成立时,才进行转成红黑树
         *                      // (2)依次和该链表的每一个元素比较过程中,如果有相同情况,就直接break
         *                 for (int binCount = 0; ; ++binCount) {
         *                     if ((e = p.next) == null) {
         *                         p.next = newNode(hash, key, value, null);
         *                         if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
         *                             treeifyBin(tab, hash);
         *                         break;
         *                     }
         *                     if (e.hash == hash &&
         *                         ((k = e.key) == key || (key != null && key.equals(k))))
         *                         break;
         *                     p = e;
         *                 }
         *             }
         *             if (e != null) { // existing mapping for key
         *                 V oldValue = e.value;
         *                 if (!onlyIfAbsent || oldValue == null)
         *                     e.value = value;
         *                 afterNodeAccess(e);
         *                 return oldValue;
         *             }
         *         }
         *         ++modCount;
         *         if (++size > threshold)
         *             resize();
         *         afterNodeInsertion(evict);
         *         return null;
         *     }
         */
        
        
        
    }
}

  • HashSet练习
package collection_.set_;

import java.util.HashSet;
import java.util.Objects;

/**
 * 1. 创建3个Employee对象放入HashSet中
 * 2. 当name和age的值相同时,认为是相同员工,不能添加到HashSet集合中
 */
public class HashSetExercise {
    public static void main(String[] args) {
        HashSet set = new HashSet();
        set.add(new Employee("john", 20));
        set.add(new Employee("smith", 10));
        set.add(new Employee("smith", 10));
        for (Object o : set) {
            System.out.println(o);
        }


    }
}

class Employee{
    private String name;
    private int age;



    public Employee(String name, int age) {
        this.name = name;
        this.age = age;
    }


    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

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

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

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



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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值