首页 > 编程语言 >集合详解——Robyn编程学习(Java)

集合详解——Robyn编程学习(Java)

时间:2022-09-29 15:34:32浏览次数:45  
标签:hash tab iterator Robyn value 详解 key Java null

集合的框架体系

本节课学习的目标:彻底搞明白集合的通用框架体系,以不变应万变。能够分清楚一个集合类型的应用与区别

首先明白collection和map的区别,collection接口时单列集合,Map接口是双列集合

Collection

Collection的遍历

可以使用Iterator迭代器来遍历Collection集合中的元素,且此迭代器本身并不存放对象。(快捷键:itit)

也可以使用增强for循环来进行遍历(快捷键:iter)

ArrayList arrayList = new ArrayList();
        arrayList.add("西游记");
        arrayList.add("三国演义");
        arrayList.add("水浒传");
        arrayList.add("红楼梦");

        //使用迭代器遍历列表,再次遍历就需要重置迭代器
        Iterator iterator = arrayList.iterator();
        while (iterator.hasNext()) {
            Object next =  iterator.next();
            System.out.println("obj="+ next);
        }

        //使用增强for循环来遍历列表
        for (Object o : arrayList) {
            System.out.println("obj=" + o);
        }

list(有序)

List接口的特征

  1. 有序(添加顺序和取出顺序一致,可重复,可以根据索引进行增删改查)
  2. 可索引
  3. 可重复

ArrayList底层代码分析

这些是通过底层代码得出的,一定要分清楚

Vector底层代码分析

Vector中有两个值得注意的点:第一是2倍扩容,第二是带有synchronized机制,能够有效实现互斥(保证了线程同步)

LinkedList底层代码分析

与AaaryList和Vector相比,LinkedList链表里的添加和删除,不是通过数组完成的,而是通过节点链表的形式。

//创建一个双向链表
        LinkedList linkedList = new LinkedList();
        linkedList.add(1);
        linkedList.add(2);
        linkedList.add(3);
        //修改节点对象
        linkedList.set(1,999);
        //遍历节点对象(这里面体现的是一种数据结构的思想)
        Iterator iterator = linkedList.iterator();
        while (iterator.hasNext()) {
            Object next =  iterator.next();
            System.out.println("next=" + next);
        }
        System.out.println("linkedList=" + linkedList);

ArrayList与Vector的区别

ArrayList与LinkedList的区别

ArrayList和LinkedList的主要区别在于由底层结构不同导致的增删效率不同。

Set

List接口的特征

  1. 无序(添加顺序和取出顺序不一致,没有索引)
  2. 没有索引
  3. 不重复(因为没有索引所以不可重复)

HashSet的底层机制说明(重要)

//1.hashset底层其实就是hashmap,只用了key,只不过是value固定的hashmap
 public HashSet() {
        map = new HashMap<>();
    }
//2.add方法
 public boolean add(E e) {//这里的e就是想要加进去的内容,这里的PRESENT是常量
        return map.put(e, PRESENT)==null;//如果是添加成功,则为T,否则为F
    }


//3.put方法
public V put(K key, V value) {
        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;
        if ((tab = table) == null || (n = tab.length) == 0)//这里就是创建这个tab数组用来存放节点
            n = (tab = resize()).length;//这里设置了tab数组的大小和临界值
        if ((p = tab[i = (n - 1) & hash]) == null)//这里是计算key对应的tab数组的索引,并且得到一个对象p
            tab[i] = newNode(hash, key, value, null);//这里吧把hash值存放的原因就是便于比较equals,key就是真正的数据,value是对应present
        else {//这个就是对于该节点有数据的判断,是在后面按照链表加入,还是拒绝加入
            Node<K,V> e; K k;//在需要的地方创建辅助变量
            if (p.hash == hash &&//当前索引位置对应的链表的第一个元素和准备添加的元素hash值相同
                ((k = p.key) == key || (key != null && key.equals(k))))//准备加入的key和p指向的node节点的key是同一个对象//或者是p指向的节点的key的equals方法和准备加入的key的计算,比较后相同
                e = p;//就不能加入
            else if (p instanceof TreeNode)//判断p是否是红黑树
                e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);//红黑树的添加
            else {
                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)//如果超过12,继续扩容
            resize();
        afterNodeInsertion(evict);//子类实现
        return null;
    }

hashset的扩容机制

LinkedHashSet底层机制

就是在hashset上补充了一个双向链表

Map

Map的遍历机制

Entry是Map的内部类,因此可以使用Entry直接转换
    Set set = hashMap.entrySet();
for (Object o : set) {
    Map.Entry m =(Map.Entry) o;
    System.out.println(m.getKey() +"-"+m.getValue());
}
//先取出所有的key列表,其实就是多了一个keyset
        Set keyset = hashMap.keySet();

        //增强for循环
        for (Object key : keyset) {
            System.out.println(key+ "-"+ hashMap.get(key));
        }
        //迭代器
        Iterator iterator = keyset.iterator();
        while (iterator.hasNext()) {
            Object key =  iterator.next();
            System.out.println(key+ "-" +hashMap.get(key));
        }

HashMap机制

Hashtable机制

public synchronized V put(K key, V value) {//具有互斥机制,跟hashmap很像
        // Make sure the value is not null
        if (value == null) {
            throw new NullPointerException();
        }

        // Makes sure the key is not already in the hashtable.
        Entry<?,?> tab[] = table;
        int hash = key.hashCode();
        int index = (hash & 0x7FFFFFFF) % tab.length;
        @SuppressWarnings("unchecked")
        Entry<K,V> entry = (Entry<K,V>)tab[index];
        for(; entry != null ; entry = entry.next) {
            if ((entry.hash == hash) && entry.key.equals(key)) {
                V old = entry.value;
                entry.value = value;
                return old;
            }
        }

        addEntry(hash, key, value, index);
        return null;
    }

Collection中的操作

为什么treeset和treemap可以排序?

 TreeSet treeSet = new TreeSet(new Comparator() {//使用Comparator接口来实现有序排序
            @Override
            public int compare(Object o1, Object o2) {
                //下面 调用String的 compareTo方法进行字符串大小比较
                //如果老韩要求加入的元素,按照长度大小排序
                //return ((String) o2).compareTo((String) o1);
                return ((String) o1).length() - ((String) o2).length();
            }
        });

集合选型

TIPS:HashSet与TreeSet实现去重的机制区别

标签:hash,tab,iterator,Robyn,value,详解,key,Java,null
From: https://www.cnblogs.com/robyn2022/p/16741758.html

相关文章

  • Java并发编程之美
    简介《Java并发编程之美》分为三部分,第一部分为Java并发编程基础篇,主要讲解Java并发编程的基础知识、线程有关的知识和并发编程中的其他相关概念,这些知识在高级篇都会有所......
  • Java并发编程之美
    简介《Java并发编程之美》分为三部分,第一部分为Java并发编程基础篇,主要讲解Java并发编程的基础知识、线程有关的知识和并发编程中的其他相关概念,这些知识在高级篇都会有所......
  • Linux下使用HttpClient或启动Tomcat报错 java.net.SocketException: 权限不够
    java.net.SocketException:权限不够报错的原因是:java.net.SocketException:Permissiondenied,明显是网络权限问题。产生该问题的原因是linux操作系统不允许非root用户使......
  • JavaScript——案例-表单验证
    需求     <!DOCTYPEhtml><htmllang="en"><head><metacharset="UTF-8"><title>Title</title></head><body><formid="reg-form"action="#"......
  • 三、java 基础提炼
    一、标识符:凡是自己命名的都是标识符1.标识符的定义规则:由26个字母大小写,0-9,_或$组成;数字不能开头;不可以用用关键字、保留字,但可以包含关键字和保留字;java严格区分大小写;标......
  • JavaScript封装方法
    1、输入一个值,返回其数据类型**2、数组去重3、字符串去重1、输入一个值,返回其数据类型**functiontype(para){returnObject.prototype.toString.call(para)......
  • JavaScript 中的全局变量 和局部变量
    JS中声明全局变量主要分为显式声明或者隐式声明。1.显示声明:使用var(关键字)+变量名(标识符)的方式在function外部声明,即为全局变量;在function内部声明的是局部变量。<scrip......
  • Java中装饰者模式
    装饰模式可以在不改变原来类中代码的基础上,增强类中的方法装饰类LDHWrapper和被装饰类LDH必须实现同个接口Star装饰类中要定义属性为:被装饰类对象,同时定义构造方法重写......
  • JavaScript——事件监听
    事件监听    1、事件绑定      2、常见事件    ......
  • java代码练习(数组)基于黑马的课自学
    代码练习数组本次练习全部使用黑马的课中给出的模型本博客的意义在于统计我平时写代码过程中的错误模型一错误代码所写代码及其运行结果可见其不符合需求原因:“......