【达内课程】集合之 LinkedList

集合介绍

简介

Java 集合类存放在 java.util 包中,是一个用来存放对象的容器,是用来收集一组数据的数据结构。

集合和数组区别

1、数组长度固定,添加数据只能新建数组;而集合类容量可动态改变。
2、数组能存放基本数据类型和对象;而集合类存放的都是对象,比如你存入一个 int 型数据 2 放入集合中,其实它是自动转换成 Integer 类后存入的,Java 中每一种基本数据类型都有对应的引用类型。(数组和集合存放的对象皆为对象的引用地址。)
3、数组无法判断其中实际存有多少元素,length 只告诉了数组的容量;而集合的 size() 可以确切知道元素的个数。

集合分类

Java 集合类型分为 Collection 和 Map。它们是 Java 集合的根接口,这两个接口又包含了一些子接口或实现类。关系如下:

java.util包
在这里插入图片描述

Collection相关方法

这些方法属于Collection类,可以被子类继承,因此通用性较强,不仅 List 能用,Set也能用。

返回类型方法名称描述
booleanadd(Object o)添加元素
intsize()获取元素个数
booleancontains(Object o)判断是否存在指定元素
booleanremove(Object o)删除元素
voidclear()清空
booleanisEmpty()判空
Object[]toArray()集合转数组

List 相关方法

List的派生类对象可以使用,Set不可用。都是和索引相关的方法:

返回类型方法名称描述
voidadd(int index, E element)指定位置添加元素
intindexOf(Object o)获取指定元素的索引
Eset(int index, E element)替换指定位置的元素,返回更新前的元素
Eget(int index)获取指定索引的元素
Eremove(int index)删除指定索引的元素

LinkedList 特色方法

特色方法解释
addFirst()头部添加
addLast()尾部添加
removeFirst()头部删除
removeLast()尾部删除
push()入栈,等效于addFirst()
pop()出栈,等效于removeFirst()
offer()入队列,等效于addLast()
poll()出队列,等效于removeFirst()

LinkedList 介绍

LinkedList 是采用双向循环链表实现的。数据封装在节点对象内部,前后两个节点,前一个引用前一个对象,后一个引用后一个对象。如果只有一个数据,那么引用自己。

ArrayList 使用数组实现,查询快,增删慢;
LinkedList 使用链表实现,查询慢,增删快。访问中间效率低,访问两端效率高,越往中间效率越低。

创建对象

LinkedList list = new LinkedList();

举例:LinkedList 基本使用

		/*
         *<>-泛型
         *   指定集合中允许存放的数据类型
         *   不支持基本类型
         *   可以使用基本类型的包装类
         *
         *   后边的泛型可以省略,默认和前面一样
         */
        LinkedList<String> list = new LinkedList<>();
        list.add("aa");
        list.add("bb");
        list.add("cc");
        list.add("dd");
        list.add("ee");
        list.add("ff");
        System.out.println(list.size());//6
        System.out.println(list);//[aa, bb, cc, dd, ee, ff]

        list.add(3, "33");
        System.out.println(list);//[aa, bb, cc, 33, dd, ee, ff]

        list.addFirst("FFF");
        list.addLast("LLL");
        System.out.println(list);//[FFF, aa, bb, cc, 33, dd, ee, ff, LLL]
        System.out.println(list.getFirst());//FFF
        System.out.println(list.getLast());//LLL
        //返回被移除的数据
        System.out.println(list.removeFirst());//FFF
        System.out.println(list.removeLast());//LLL
        System.out.println(list);//[aa, bb, cc, 33, dd, ee, ff]
        System.out.println(list.get(0));//aa
        System.out.println(list.get(list.size() - 1));//ff
        //返回被移除的数据
        System.out.println(list.remove(5));//ee
        System.out.println(list);//[aa, bb, cc, 33, dd, ff]
        //指定数据移除,如果找到这个数据,移除它返回true;如果没找到这个数据,返回false
        //找到第一个相同的数据移除,后面的不被移除
        System.out.println(list.remove("ccc"));//false
        //返回被替换的值
        System.out.println(list.set(4, "444"));
        System.out.println(list);//[aa, bb, cc, 33, 444, ff]
        //下标遍历,效率并不高,需要从0开始数
        for (int i = 0; i < list.size(); i++) {
            System.out.println(list.get(i));
        }

前边的输出结果放到程序注释里了,最后的遍历结果:

aa
bb
cc
33
444
ff

双向链表下标遍历效率低,每次访问一个下标的值,都要从头遍历,而 Iterator(迭代器)能更高效的遍历

改用迭代器遍历

		//迭代器遍历
    	//对于双向链表,迭代器遍历效率高
    	//新建迭代器,通过 list 的 iterator 新建迭代器,iterator() 实际上就是 return new Iterator
    	Iterator<String> it = list.iterator();
        while (it.hasNext()) {
            System.out.println(it.next());
        }

举例:求第n个丑数

丑数就是只包含因子2,3,5的数(Ugly Number),习惯上我们把 1 当做是第一个丑数

判断方法:首先除 2,直到不能整除为止,然后除 5 到不能整除为止,然后除 3 直到不能整除为止。最终判断剩余的数字是否为 1,如果是 1 则为丑数,否则不是丑数。

public class Main {
    public static void main(String[] args) throws ParseException {
        System.out.println("求第n个丑数");
        int n = new Scanner(System.in).nextInt();
        long r = f(n);
        System.out.println(r);
    }

    private static long f(int n) {
        //计数变量
        int count = 0;
        //保存结果变量
        long r = 0;
        //无限循环从1开始递增
        for (int i = 1; ; i++) {
            //判断i是不是丑数
            //新的变量保存i的值
            int j = i;
            while (j % 2 == 0) {
                j /= 2;
            }
            while (j % 3 == 0) {
                j /= 3;
            }
            while (j % 5 == 0) {
                j /= 5;
            }

            if (j == 1) {
                //i是整数
                count++;
                if (count == n) {
                    r = i;
                    break;
                }
            }
        }

        return r;
    }
}

运行结果
在这里插入图片描述

举例:求第n个丑数进阶版(高效率)

想办法从上一个丑数推断出下一个丑数,而不需要从 1 开始遍历再判断。

从 1 开始的10个丑数分别为1,2,3,4,5,6,8,9,10,12。可以发现除了 1 以外,丑数都是由 某个丑数2 或者 3 或者 5 得到的。如2是丑数 12 得到的,3 是丑数 13 得到的,4 是丑数 14 得到的,5 是丑数 15 得到的,6 是丑数 23 得到的……

所以我们思路如下:

1、准备3个集合,先分别放入2、3、5

2
3
5

2、把最小值取出来(2),分别乘 2、3、5,分别放入3个集合。最后移除最小值。
4
3、6
5、10

第一次的丑数我们得到了就是最小值2

3、重复以上步骤,取出最小值(3),分别乘 2、3、5,分别放入 3 个集合。移除最小值。
4、6、
6、9、
5、10、15、

此时我们得到了丑数:2  3

再来一次,取出最小值(4),分别乘 2、3、5,分别放入3个集合。移除最小值。
6、8、
6、9、12、
5、10、15、20、
此时我们得到了丑数:2  3  4

4、重复以上步骤

有重复的最小值都要取出

5、第一次执行得到第1个丑数,第二次执行得到第2个…第n次执行得到第n个丑数

这里我们没有包含默认的 1 为丑数,这里我们把 2 当作了第一个丑数。

直接看程序:

public class Main {
    public static void main(String[] args) throws ParseException {
        System.out.println("求第n个丑数");
        int n = new Scanner(System.in).nextInt();
        long r = f(n);
        System.out.println(r);
    }

    private static long f(int n) {
        //创建3个集合
        LinkedList<Long> list2 = new LinkedList();
        LinkedList<Long> list3 = new LinkedList();
        LinkedList<Long> list5 = new LinkedList();
        //初始值分别放入2、3、5
        list2.add(2L);
        list3.add(3L);
        list5.add(5L);
        //保存结果
        long r = 0;
        for (int i = 1; i <= n; i++) {
            //访问3个头部值
            long a = list2.getFirst();
            long b = list3.getFirst();
            long c = list5.getFirst();
            //最小值赋给r
            r = Math.min(a, Math.min(b, c));
            //移除最小值
            if (r == a) {
                list2.removeFirst();
            }
            if (r == b) {
                list3.removeFirst();
            }
            if (r == c) {
                list5.removeFirst();
            }
            //r分别乘2、3、5放入三个集合
            list2.add(r * 2);
            list3.add(r * 3);
            list5.add(r * 5);

        }
        return r;
    }
}

运行程序可以看到速度是非常快的:
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值