Java数据结构探索

目录


堆(优先队列)

好文章借鉴

Java集合中List,Set以及Map等集合体系详解(史上最全)

//小顶堆(从小到大)  使用Lambda表达式实现内部排列的写法(写法一,最便捷)
PriorityQueue<Integer> pri = new PriorityQueue<Integer>((o1, o2)->o1 - o2);
pri.add(1);//加入堆
pri.poll();//弹出堆顶元素
pri.size();//堆的元素数量
pri.add(22);
pri.add(3);
while(pri.size() > 0) {
    System.out.println(pri.poll());
}

PriorityQueue<Integer> pri = new PriorityQueue<Integer>(new Comparator<Integer>(){
    @Override
    public int compare(Integer o1,Integer o2){
        return o1 - o2;//实现小顶堆
    }
});

结果
3
22

 //大顶堆(从大到小)
PriorityQueue<Integer> pri = new PriorityQueue<Integer>((o1,o2)->o2 - o1); 
pri.add(1);//加入堆
pri.poll();//弹出堆顶元素
pri.size();//堆的元素数量
pri.add(22);
pri.add(3);
while(pri.size() > 0) {
    System.out.println(pri.poll());
}

22
3

如果PriorityQueue排序对象非Integer基本数据类型,比如像int[]这种类型,按照先按照int[]数组的第2个数进行从小到大排序,否则再按照int[]数组的第3个数进行从小到大排序,最后按照int[]数组的第1个数进行从小到大排序,排序写法

PriorityQueue<int[]> pri = new PriorityQueue<int[]>((o1,o2)-> {
    if (o1[1] != o2[1]) {
        return o1[1] - o2[1];
    } else {
        if (o1[2] != o2[2]) {
            return o1[2] - o2[2];
        } else {
            return o1[0] - o2[0];
        }
    }
});

栈和队列

在Java中

  • LinkedList通常可以作为使用
  • ArrayDeque通常作为队列使用
  • ProporityQueue用来实现

java堆栈队列api

//队首出队,队尾进队

//栈(LinkedList)的API
LinkedList<Integer> stack = new LinkedList();//创建新栈
stack.push(1);//向栈顶压入元素1(注意这里一定要用push,而不能用offer、add这种API,不然这又变成了队列)
stack.pop();//从栈顶弹出元素(会返回该元素)并删除
stack.peek();//从栈顶查看元素不弹出(不删除) 等同于getFirst()
stack.isEmpty();//判断栈是否为空
stack.size();//判断此时栈内大小
//队列(Deque)的API(记住队尾入队,队首出队,ArrayDeque对象中下标为0的永远是队首)
Deque<String> queue = new ArrayDeque<>();
queue.offer("a");//向队列尾部添加元素  等同于add()、addLast()都是在队尾添加元素,  (注意addFirst()是往队首增加元素)
queue.poll();//向队列头部弹出元素(会返回该元素)并删除 等同于remove()、removeFirst()
queue.peek();//返回队首元素(不删除)等同于getFirst()
queue.isEmpty();//判断deque是否为空
queue.size();//判断此时队列长度
queue.getLast()//双端队列,获取队尾元素
queue.removeFirst();//双端队列,删除队首元素

ArrayDeuqe和LinkedList之间的比较:
If you need add/remove of the both ends, ArrayDeque is significantly better than a linked list. Random access each element is also O(1) for a cyclic queue.
The only better operation of a linked list is removing the current element during iteration.

如果需要对两端进行添加/删除,ArrayDeque明显优于链表。对于循环队列,随机访问每个元素也是O(1)。
更好的链表操作是在迭代期间删除当前元素。

做了以下实验:
测试设置考虑:
每个测试对象是一个500字符的字符串。每个String都是内存中的不同对象。
测试数组的大小将在测试期间变化。
对于每个数组大小/队列实现组合,将运行100个测试,并计算每个测试的平均时间。
每个测试都用所有对象填充每个队列,然后将它们全部删除。
以毫秒为单位测量时间。
测试结果:
在10,000个元素以下,LinkedList和ArrayDeque测试的平均水平都低于1毫秒。
随着数据集越来越大,ArrayDeque和LinkedList之间的平均测试时间的差异也越来越大。
在9,900,000个元素的测试大小下,LinkedList方法比ArrayDeque方法多花了大约165%的时间。
综上,如果LinkedList和ArrayDeque具备的API都可以实现功能,优先使用ArrayDeque.

字符串

字符串获取某个下标的字符charAt

  1. 通过charAt 遍历字符串 通过下标返回一个char类型对象,可以进行便利操作
String str = "123";
for(int i = 0;i < str.length();i++){
    System.out.println(str.charAt(i));
}

字符串获取子串 substring

substring函数是Java字符串类的一个内置方法,用于从给定的字符串中获取一个子字符串。这个方法有两种签名,一种是只传入一个开始索引,另一种是传入一个开始索引和一个结束索引。例如:

String s = "Hello World";
String sub1 = s.substring(6); // sub1 = "World"
String sub2 = s.substring(0, 5); // sub2 = "Hello"

这个方法的含义是:

  • substring(int beginIndex) 返回从指定的开始索引beginIndex开始到字符串的最后一个字符的子字符串。例如:“Chaitanya”.substring(2) 返回 “aitanya”。开始索引是包含在子字符串中的,所以索引为2的字符也被包括在内。
  • substring(int beginIndex, int endIndex) 返回从指定的开始索引beginIndex开始到指定的结束索引endIndex减一的子字符串。例如:“Beginnersbook”.substring(9, 13) 返回 “book”。开始索引是包含在子字符串中的,但结束索引是不包含在子字符串中的,所以索引为13的字符被排除在外。
for(int i = 0;i < str.length();i++){
  System.out.println(str.substring(i,i + 1));
}

String与字符数组之间转换

  1. String到字符数组
//方法一
String s = "abc";
char[] chs = s.toCharArray();
  1. 字符数组到String
//方法二
char[] chs = {'a','b','c'};
String s = new String(chs);
  1. 字符串到字符数组后进行遍历
String s = "sdsds";
char[] chs = s.toCharArray();
for(char c : chs){
	System.out.println(c);
}

String类型加入ArrayList后,如果对于加入的字符串进行更改,ArrayList中的字符串对象也会被更改吗?还是保持不变?

public static void main(String[] args) {
     ArrayList<String> arr = new ArrayList<>();
     String s = "before";
     arr.add(s);
     System.out.println("改变前:" + arr);
     s = "after";
     System.out.println("改变后:" + arr);
 }

保持不变,结果如下:

改变前:[before]
改变后:[before]

理由如下:
如果您对加入 ArrayList 的字符串进行更改,ArrayList 中的字符串对象不会被更改,而是保持不变。这是因为 String 类是不可变的,也就是说,一旦创建了一个 String 对象,它的值就不能被修改。当您对一个 String 对象赋予一个新的值时,实际上是创建了一个新的 String 对象,并让原来的对象指向它。而 ArrayList 中存储的是对象的引用,所以当您更改字符串时,ArrayList 中的引用并没有改变,仍然指向原来的对象。

String&StringBuilder的使用

StringBuilder增加和删除元素

根据网上的信息,StringBuilder是Java中的一个类,用于创建和修改可变的字符串。StringBuilder类提供了一些方法来增加和删除元素,例如:

append方法,用于在字符串的末尾添加一个元素或者一个子字符串。例如:

StringBuilder sb = new StringBuilder("Hello");
sb.append(' '); // sb = "Hello "
sb.append("World"); // sb = "Hello World"

insert方法,用于在字符串的指定位置插入一个元素或者一个子字符串。例如:

StringBuilder sb = new StringBuilder("Hello");
sb.insert(5, ' '); // sb = "Hello "
sb.insert(6, "World"); // sb = "Hello World"

delete方法,用于删除字符串中指定范围的元素或者子字符串。例如:

StringBuilder sb = new StringBuilder("Hello World");
sb.delete(5, 6); // sb = "HelloWorld"
sb.delete(5, 11); // sb = "Hello"

deleteCharAt方法,用于删除字符串中指定位置的元素。例如:

StringBuilder sb = new StringBuilder("Hello World");
sb.deleteCharAt(5); // sb = "HelloWorld"
sb.deleteCharAt(4); // sb = "HellWorld"

StringBuilder获取长度

StringBuilder sb = new StringBuilder();
sb.length();

HashMap、TreeMap、LinkedHashMap的区别

HashMap是无序排列(不会按照插入顺序,或者按照某种顺序)。适合只需要快速访问的map的场景。

LinkedHashMap会按照插入顺序存储元素,适合读取按照插入顺序的场景。

TreeMap会按照默认顺序排序(比如说,数字按照从小到大的顺序,字符按照字典的顺序)
在这里插入图片描述

HashMap、LinkedHashMap和TreeMap和ConcurrentHashMap底层实现

Map用法

HashMap用法参考链接

//1.初始化
HashMap<String,Integer> map = new HashMap<>();
//2.插入存入新值
System.out.println("插入新值:'测试',1");
map.put("测试",1);
//3.查找 获取新值
//(1).get查找
System.out.println("按照'测试'获取对应值:" + map.get("测试"));
if(map.get("未找到") == null){
    System.out.println("按照'测试'获取对应值,没有存入键返回null");
}
//(2).getOrDefault查找
System.out.println("如果根据键1查找不到对应值,则返回默认值-1,map.getOrDefault('1',-1)输出结果:" + map.getOrDefault("1",-1));//如果根据键1查找不到对应值,则返回默认值-1
//4.判断某个元素是否存在   其实可以用get来判断,如果不存在则返回null
System.out.println("查找是否存在'测试'这个键:"+map.containsKey("测试"));
//5.删除元素
map.remove("测试");//根据key值删除
System.out.println("删除remove后查找是否存在'测试'这个键:"+map.containsKey("测试"));
//6.长度
map.put("测试",1);
System.out.println("重新增加'测试':1这个数据,长度为:" + map.size());
//7.循环遍历
//7.1增强for循环
for(String key : map.keySet()){//注意为keySet,不是entrySet
    System.out.println("key:" + key + ",对应value:" + map.get(key));
}
//7.2Iterator遍历
Iterator it = linkedHashMap.entrySet().iterator();//注意为entrySet,不是keySet
while (it.hasNext()) {
    Map.Entry entry = (Map.Entry) it.next();
    System.out.println("key:" + entry.getKey() + "   value:" + entry.getValue());
}
//8.对于HashMap对象,在循环其元素时,遇到特定键将对应键值对删除,但是又不影响整体的遍历
/**
* 对于HashMap对象,在循环其元素时,遇到特定键将对应键值对删除,但是又不影响整体的遍历
* 是使用Iterator来遍历HashMap对象,并使用Iterator的remove()方法来删除特定元素。
* 有一个HashMap<String, Integer>,存储了一些字符串和它们的长度,你想要删除其中键为"Java"的键值对
* @param args
*/
public static void main(String[] args) {
   // 创建一个HashMap<String, Integer>对象
   HashMap<String, Integer> map = new HashMap<> ();
   map.put ("Hello", 5);
   map.put ("Java", 4);
   map.put ("World", 5);
   map.put ("Bing", 4);

   // 获取一个Iterator对象
   Iterator<Map.Entry<String,Integer>> iterator = map.entrySet().iterator();
   // 遍历HashMap对象
   while(iterator.hasNext()){
       // 获取当前元素
       Map.Entry<String,Integer> entry = iterator.next();
       // 获取当前元素的值
       String key = entry.getKey ();
       // 判断是否等于4
       if ("Java".equals(key)) {
           // 删除当前元素
           iterator.remove ();
       }
   }

   // 打印最终的HashMap对象
   System.out.println (map);
}

为TreeMap对象设置一个Comparator外部排序函数,实现逆序排序(从大到小)下面以Integer对象举例

TreeMap<Integer,String> map = new TreeMap(new Comparator<Integer>(){
    @Override
    public int compare(Integer o1,Integer o2){
        return o2 - o1;// o2 - o1逆序排序;  如果是o1 - o2是正序排序,正序就是越小越在前面
    }
});
map.put(12,"Str12");
map.put(78,"Str78");
for(Integer i : map.keySet()){
    System.out.println(i);
}
//第2种遍历的写法
//        Iterator iter = map.entrySet().iterator();
//        while(iter.hasNext()){
//            Map.Entry<Integer,String> entry = (Map.Entry<Integer, String>) iter.next();
//            System.out.println(entry.getKey() + " " + entry.getValue());
//        }

结果
78 Str78
12 Str12

为TreeSet对象设置一个Comparator外部排序函数,实现正序排序(从小到大)下面以Integer对象举例

TreeSet<Integer> set = new TreeSet<Integer>(new Comparator<Integer>(){
    @Override
    public int compare(Integer o1,Integer o2){
        return o1 - o2;// 如果是o1 - o2是正序排序;    o2 - o1逆序排序
    }
});
set.add(588);
set.add(1);
Iterator iter = set.iterator();
while(iter.hasNext()){
    System.out.println(iter.next());
}

结果
1
588

为TreeSet中自定义对象设置Comparator外部排序同时实现Comparable接口,实现正序排序(从小到大)下面以Person自定义对象举例:

import java.util.Comparator;
import java.util.TreeSet;

public class Person implements Comparable<Person>{//注意这里排序Person,泛型也是Person

    public Integer age;

    public Person(Integer age){
        this.age = age;
    }

    @Override
    public int compareTo(Person comparePerson){
        return age - comparePerson.age;//正序,越小越在前
    }


    public static void main(String[] args) {
        Person person2 = new Person(13);
        Person person = new Person(12);

        TreeSet<Person> set = new TreeSet<>(new Comparator<Person>(){
           @Override
           public int compare(Person p1,Person p2){
               return p2.age - p1.age;//Comparator的优先级高于Comparable,并且第一个参数-第2个参数是正序
           }
        });
        set.add(person2);
        set.add(person);
        for(Person p : set){
            System.out.println(p.age);
        }
    }
}

HashSet、TreeSet、LinkedHashSet的区别

HashSet是无序排列(不会按照插入顺序,或者按照某种顺序)。适合只需要快速访问的set的场景。

  • LinkedHashMap<Integer, String> map = new LinkedHashMap<>();
    LinkedHashSet会按照插入顺序存储元素,适合读取按照插入顺序的场景。
// 创建一个按照插入顺序排序的LinkedHashMap
LinkedHashMap<Integer, String> map = new LinkedHashMap<>();
// 添加四个键值对
map.put(1, "A");
map.put(2, "B");
map.put(3, "C");
map.put(4, "D");
// 打印LinkedHashMap的键值对
System.out.println(map); // 输出 {1=A, 2=B, 3=C, 4=D}
// 访问键为2的键值对
map.get(2);
// 打印LinkedHashMap的键值对
System.out.println(map); // 输出 {1=A, 3=C, 4=D, 2=B}
// 访问键为4的键值对
map.get(4);
// 打印LinkedHashMap的键值对
System.out.println(map); // 输出 {1=A, 3=C, 2=B, 4=D}

{1=A, 2=B, 3=C, 4=D}
{1=A, 2=B, 3=C, 4=D}
{1=A, 2=B, 3=C, 4=D}

  • LinkedHashMap<Integer, String> map = new LinkedHashMap<>(16, 0.75f, true);
    LinkedHashSet会按照插入顺序存储元素,适合读取按照插入顺序的场景。
// 创建一个按照访问顺序排序的LinkedHashMap
LinkedHashMap<Integer, String> map = new LinkedHashMap<>(16, 0.75f, true);
// 添加四个键值对
map.put(1, "A");
map.put(2, "B");
map.put(3, "C");
map.put(4, "D");
// 打印LinkedHashMap的键值对
System.out.println(map); // 输出 {1=A, 2=B, 3=C, 4=D}
// 访问键为2的键值对
map.get(2);
// 打印LinkedHashMap的键值对
System.out.println(map); // 输出 {1=A, 3=C, 4=D, 2=B}
// 访问键为4的键值对
map.get(4);
// 打印LinkedHashMap的键值对
System.out.println(map); // 输出 {1=A, 3=C, 2=B, 4=D}

{1=A, 2=B, 3=C, 4=D}
{1=A, 3=C, 4=D, 2=B}
{1=A, 3=C, 2=B, 4=D}

TreeSet会按照默认顺序排序(比如说,数字按照从小到大的顺序,字符按照字典的顺序)

//从小到大排序
TreeMap<Integer,Integer> map = new TreeMap<>((o1,o2)->{
  	return o1 - o2; 
});
//从大到小排序
TreeMap<Integer,Integer> map = new TreeMap<>((o1,o2)->{
  	return o1 - o2; 
});
//lambda表达式按照字典序排序
TreeMap<String,Integer> map = new TreeMap<>((o1,o2)-> (o1.compareTo(o2)));
map.put("boy",2);
map.put("apple",1);
for (Map.Entry<String,Integer> m : map.entrySet()){
    System.out.println(m.getKey());
}

Set用法

public static void main(String[] args){
    //创建set对象
    Set<Integer> set = new HashSet<>();
    //1.向set中加入元素
    set.add(1);
    System.out.println("加入值为1的元素之后:" + set);
    //2.查找set对象
    System.out.println("是否存在值为1的元素:" + set.contains(1));
    //3.set对象的长度  size
    set.add(1);
    set.add(3);
    System.out.println("加入值为1,3的元素之后:" + set);
    System.out.println("现在set对象的长度:" + set.size());
    //4.删除set对象
    set.remove(1);
    System.out.println("删除元素1之后:" + set);
    //5.判断集合是否为空
    System.out.println("set对象是否为空:" + set.isEmpty());
    //6.清空集合
    set.clear();
    System.out.println("清空后,set对象是否为空:" + set.isEmpty());
}

ArrayList的用法

1. 遍历方式

import java.util.*;
 
public class Test{
    public static void main(String[] args){
        List<String> list = new ArrayList<String>();
        list.add("Hello");
        list.add("World");
        list.add("Hello");
        
        //第一种方法: for-each遍历 list
        for(String str : list){          //同for(int i = 0;i<list.size();i++)
            System.out.println(str);
        }
        
        //第二种方法: 把链表变为数组相关的内容进行遍历
        String[] strArray = new String[list.size()];
        list.toArray(strArray);
        for(String str: strArray){
            System.out.println(strArray[i]);
        }
        
        //第三种方法: 使用迭代器
        //第三种方法不用担心在遍历过程中超过集合的长度
        Iterator<String> ite = list.iterator();
        while(ite.hasNext()){
            System.out.println(ite.next());
        }
    }
}

2. [插入新值]

  • 在尾部插入数据

3.修改值(根据下标)

ArrayList<String> list = new ArrayList<>();
list.add("Oscar");
list.set(0,"Jack");//第一个参数要修改的下标  第二个参数是要修改的值(将Oscar 换成 jack)

4.获取列表末尾元素

ArrayList<String> list = new ArrayList<>();
list.add("Oscar");
list.add("Jack");
//获取末尾元素
System.out.println(list.get(list.size() - 1));

Jack

5.对于ArrayList对象,在循环其元素时,遇到特定元素将其删除,

/**
* 对于ArrayList对象,在循环其元素时,遇到特定元素将其删除,但是又不影响整体的循环遍历,试写出对应java代码
* 使用Iterator来遍历ArrayList对象,并使用Iterator的remove()方法来删除特定元素。
* 例如,假设你有一个ArrayList<String>,存储了一些字符串,你想要删除其中包含"Java"的字符串
* @param args
*/
public static void main(String[] args) {
    // 创建一个ArrayList<String>对象
    ArrayList<String> list = new ArrayList<> ();
    list.add ("Hello");
    list.add ("Java");
    list.add ("World");
    list.add ("Bing");

    // 获取一个Iterator对象
    Iterator<String> iterator = list.iterator ();

    // 遍历ArrayList对象
    while (iterator.hasNext ()) {
        // 获取当前元素
        String element = iterator.next ();
        // 判断是否包含"Java"
        if (element.contains ("Java")) {
            // 删除当前元素
            iterator.remove ();
        }
    }

    // 打印最终的ArrayList对象
    System.out.println (list);
}

LinkedList的用法

1.获取末尾元素

LinkedList<Integer> list = new LinkedList();//创建新栈
list.add("Oscar");
list.add("Jack");
//获取末尾元素
System.out.println(list.getLast());

Jack

2.删除末尾元素

LinkedList<String> list = new LinkedList();//创建新栈
list.add("Oscar");
list.add("Jack");
list.removeLast();//删除末尾元素
//获取末尾元素
System.out.println(list.getLast());

Oscar

如果没有成功删除末尾元素,末尾元素就应该是Jack;现在展示的是Oscar,则说明末尾元素删除成功。

创建数组

int[] arr1 = new int[10];//注意最后没有()
int[] arr2 = {1,2,3};

求数的幂

求2的5次方

//使用pow这个API,但是返回数据类型为double,所以需要用int进行格式转换
(int)Math.pow(2,5);

求两数的绝对值

int a = 5,b = 10;
Math.abs(a - b);

求两数的最大值

int a = 5,b = 10;
Math.max(a - b);

Arrays 对于数组进行操作

Arrays将数组转化为 List对象

 Integer[] a = new Integer[]{1,2,3};
 List<Integer> arrList = Arrays.asList(a);//这里的数组对象必须为对象,也就是如果是int[]就不能转换成为List列表了

输出:ArrayList中{1,2,3}

int[] arr = {3,2,4};
Arrays.sort(arr);
for(int i : arr){
   System.out.println(i);
}
//2,3,4
int[] in = new int[2];
Arrays.fill(in,0);//将in中的所有元素
for(int i : in){
   System.out.println(i);
}
//0 0
比较两个数组内容是否相等
int[] tar = {1,2};
int[] com = {1,2};
Arrays.equals(tar,com);

Collections

List<Integer> result = new ArrayList();
//将ArrayList排序  (默认从小到大顺序)
Collections.sort(result);
//将ArrayList排序  (从大到小顺序)
Collections.sort(result,(n1,n2) -> n2 - n1);
//将ArrayList倒序
Collections.reverse(result);

报错 cannot find symbol [in Driver.java]

leetcode报错error: cannot find symbol [in Driver.java]

主函数名字不一样导致编译出错。把主函数改过来就好了

怎么将小数点保留2位小数(如果题目没有特殊说明,就是直接截取2位小数)

//小数是num,类型double
可以通过System.out.printf(“%.2f”,num);//直接截取小数点后2位小数
//通过BigDecimal可以实现小数点取舍进位
//四舍五入

double sum = 0;
BigDecimal big = new BigDecimal(sum);
//保留2位小数,四舍五入
doule sum = big.setScale(2,BigDecimal.ROUND_HALF_UP).doubleValue();

计算平方根(double类型)

使用 double sqrt(double)

double num = 0;
num = Math.sqrt(num);

获取输入

BufferedReader bf = new BufferedReader(new InputStreamReader(System.in));
String s = bf.readLine();//读取整行数据(直到换行符)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值