java基础(4)注解,集合,

注解

什么是注解(Annotation)?注解是放在Java源码的类、方法、字段、参数前的一种特殊“注释”

// this is a component:
@Resource("hello")
public class Hello {@Injectint n;@PostConstructpublic void hello(@Param String name) {System.out.println(name);}@Overridepublic String toString() {return "Hello";}
}

注释会被编译器直接忽略,注解则可以被编译器打包进入class文件,因此,注解是一种用作标注的“元数据”。

注解的作用

从JVM的角度看,注解本身对代码逻辑没有任何影响,如何使用注解完全由工具决定。
Java的注解可以分为三类:

  • 由编译器使用的注解,
例如:@Override:让编译器检查该方法是否正确地实现了覆写;
@SuppressWarnings:告诉编译器忽略此处代码产生的警告

这类注解不会被编译进class文件中。

  • 第二类是由工具处理.class文件使用的注解,比如有些工具会在加载class的时候,对class做动态修改,实现一些特殊的功能。一般只被底层库使用,不必自己处理。
  • 第三类是在程序运行期能够读取的注解,它们在加载后一直存在于JVM中,这也是最常用的注解。例如,一个配置了@PostConstruct的方法会在调用构造方法后自动被调用(这是Java代码读取该注解实现的功能,JVM并不会识别该注解)

定义一个注解时,还可以定义配置参数。配置参数可以包括:
所有基本类型; String; 枚举类型; 基本类型、String、Class以及枚举的数组。

public class Hello {@Check(min=0, max=100, value=55)public int n;@Check(value=99)public int p;@Check(99) // @Check(value=99)public int x;@Checkpublic int y;
}

@Check就是一个注解。第一个@Check(min=0, max=100, value=55)明确定义了三个参数,第二个@Check(value=99)只定义了一个value参数,它实际上和@Check(99)是完全一样的。最后一个@Check表示所有参数都使用默认值。

定义注解

java 用@interface定义注解,然后添加参数、默认值:

public @interface Report {int type() default 0;String level() default "info";String value() default "";
}

接着有一些可以用于注解的注解,成为元注解。如

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Report {int type() default 0;String level() default "info";String value() default "";
}

@Target定义该注解能注解什么东西
类或接口:ElementType.TYPE;
字段:ElementType.FIELD;
方法:ElementType.METHOD;
构造方法:ElementType.CONSTRUCTOR;
方法参数:ElementType.PARAMETER。

@Retention定义该注解的生命周期,如上就是定义在runtime的时候运行,因为我们自己写的注解一般在runtime的时候执行。
仅编译期:RetentionPolicy.SOURCE;
仅class文件:RetentionPolicy.CLASS;
运行期:RetentionPolicy.RUNTIME。
其次还有@Repeatable定义该注解是否可以重复使用在同一个地方,@Inherited定义是否可以继承父类的注解。

处理注解

Java的注解本身对代码逻辑没有任何影响。根据@Retention的配置:

  • SOURCE类型的注解在编译期就被丢掉了;
  • CLASS类型的注解仅保存在class文件中,它们不会被加载进JVM;
  • RUNTIME类型的注解会被加载进JVM,并且在运行期可以被程序读取。
使用注解
// 定义一个注解
@Retention(RetentionPolicy.RUNTIME) // runtime时运行,会打入jvm
@Target(ElementType.FIELD) // 作用于字段
@interface Range {int min() default 0;int max() default 255;
}class Person1 {@Range(min = 1, max = 20)public String name;@Range(max = 10)public String city;
}

定义了注解,本身对程序逻辑没有任何影响。我们必须自己编写代码来使用注解。

void check(Person person) throws IllegalArgumentException, ReflectiveOperationException {// 遍历所有Field:for (Field field : person.getClass().getFields()) {// 获取Field定义的@Range:Range range = field.getAnnotation(Range.class);// 如果@Range存在:if (range != null) {// 获取Field的值:Object value = field.get(person);// 如果值是String:if (value instanceof String s) {// 判断值是否满足@Range的min/max:if (s.length() < range.min() || s.length() > range.max()) {throw new IllegalArgumentException("Invalid field: " + field.getName());}}}}
}

集合

什么是集合(Collection)?集合就是“由若干个确定的元素所构成的整体”。
在Java中,如果一个Java对象可以在内部持有若干其他Java对象(注意是对象,而不是基础类型,比如Integer可以,但是int不行),并对外提供访问接口,我们把这种Java对象称为集合。很显然,Java的数组可以看作是一种集合:

String[] ss = new String[10]; // 可以持有10个String对象
ss[0] = "Hello"; // 可以放入String对象
String first = ss[0]; // 可以获取String对象

数组的限制,长度固定,数据只能通过索引获取。
我们需要各种不同类型的集合类来处理不同的数据,例如:

  • 可变大小的顺序链表;
  • 保证无重复元素的集合;
Collection

Java标准库自带的java.util包提供了集合类:Collection,它是除Map外所有其他集合类的根接口
ava的java.util包主要提供了以下三种类型的集合:

  • List 一种有序列表的集合,例如,按索引排列的Student的List,如ArrayList, LinkedList
  • Set 一种保证没有重复元素的集合,例如,所有无重复名称的Student的Set
  • Map 一种通过键值(key-value)查找的映射表集合,例如,根据Student的name查找对应Student的Map

Java集合设计的特点

  • 实现了 接口和 实现类 相分离,如有序表的接口是List,具体的实现类有ArrayList,LinkedList等
  • 支持泛型 List list = new ArrayList<>(); // 只能放入String类型
  • 访问集合都是通过迭代器(Iterator)的形式访问的,它最明显的好处在于无需知道集合内部元素是按什么方式存储的。(类似于js的实现Symbol.iterator,实现对象的迭代,外部无需知道他是怎么存储的。)
List

List是最基础的一种集合:它是一种有序列,数组和List非常相似。但是数组进行增删操作会非常麻烦。反观List
当我们需要增删操作的时候,可以使用ArrayList,

如 一个ArrayList拥有5个元素,实际数组大小为6(即有一个空位)

  • 当添加一个元素并指定索引如2到ArrayList时,ArrayList自动移动需要移动的元素,把2及后面的元素移动。
  • 然后,往内部指定索引的数组位置添加一个元素,然后把size加1
  • 此时继续添加元素的时候,数组已满,没有空闲位置的时候,ArrayList先创建一个更大的新数组,然后把旧数组的所有元素复制到新数组,紧接着用新数组取代旧数组。

可见,ArrayList把添加和删除的操作封装起来,让我们操作List类似于操作数组,却不用关心内部元素如何移动。
List < T >主要的接口方法
在末尾添加一个元素:boolean add(E e)
在指定索引添加一个元素:boolean add(int index, E e)
删除指定索引的元素:E remove(int index)
删除某个元素:boolean remove(Object e)
获取指定索引的元素:E get(int index)
获取链表大小(包含元素的个数):int size()

上述是通过数组实现的List,实际上,使用链表也能实现。
在LinkedList中,它的内部每个元素都指向下一个元素:

        ┌───┬───┐   ┌───┬───┐   ┌───┬───┐   ┌───┬───┐
HEAD ──>│ A │ ●─┼──>│ B │ ●─┼──>│ C │ ●─┼──>│ D │   │└───┴───┘   └───┴───┘   └───┴───┘   └───┴───┘

我们来比较一下ArrayList和LinkedList:

123ArrayListLinkedList
获取指定元素速度很快需要从头开始查找元素
添加元素到末尾速度很快速度很快
在指定位置添加/删除需要移动元素不需要移动元素
内存占用较大

使用

	   List<String> list = new ArrayList<>();list.add("apple");list.add("pear");List<String> list1 = new LinkedList<>();list1.add("test1");list1.add("test2");// Java 9 List<Integer> list2 = List.of(1, 2, 5);System.out.println(list);System.out.println(list1);

遍历List,使用for循环

 for (int i = 0; i < list.size(); i++) {String s = list.get(i);System.out.println(s);}

这种方法并不推荐
一是代码复杂,二是因为get(int)方法只有ArrayList的实现是高效的,换成LinkedList后,索引越大,访问速度越慢。
要始终坚持使用迭代器Iterator来访问List。Iterator本身也是一个对象,但它是由List的实例调用iterator()方法的时候创建的。

 for (Iterator<String> it = list.iterator();it.hasNext();) {String s = it.next();System.out.println(s);}

调用iterator获取一个iterator对象,跟js的迭代器很类似。it.hasNext()判断是否还有下一个元素,it.next()返回下一个元素
以上是一种固定写法,第二个参数需要搭配it.hasNext()返回一个boolen判断是否继续循环。
Java的for each循环本身就可以帮我们使用Iterator遍历

for (String it1 : list1) {System.out.println(it1);}

可以直接遍历获取值。
实际上,只要实现了Iterable接口的集合类都可以直接用for each循环来遍历,Java编译器本身并不知道如何遍历集合对象,但它会自动把for each循环变成Iterator的调用原因就在于Iterable接口定义了一个Iterator<E> iterator()方法,强迫集合类必须返回一个Iterator实例。
有点像js中,只要实现了Symbol.iterator方法的对象,就可以使用for of进行遍历。因为Symbol.iteraotr方法返回了一个迭代器对象。

List和Array互相转换
  • 把List变为Array有三种方法,第一种是调用toArray()方法直接返回一个Object[]数组,不常用,因为类型可能会丢失
  • 第二种方式是给toArray(T[])传入一个类型相同的Array,List内部自动把元素复制到传入的Array中。这里的T和List< E>不一样,所以toArray可以传其他类型的数组,但不兼容会报错。
  List<String> list1 = new LinkedList<>();list1.add("test1");list1.add("test2");String[] test2 = list1.toArray(new String[2]);Integer[] test3 = list1.toArray(new Integer[2]); //会报错

传入的数组如果不够大,那么List内部会创建一个新的数组,大小一样。
如果传入的数组过大,那么多余的元素全部填充null。

Array to List
// JAVA 9 
Integer[] array = { 1, 2, 3 };
List<Integer> list = List.of(array);// JAVA 9 之前List<String> list3 = Arrays.asList(test2);

注意,这种返回的List不一定是ArrayList或者LinkedList,因为返回的List是只读的,在操作会报错。’

编写equals方法

List提供了contains方法,来判断当前list是否包含哪些内容。提供了indexOf(obj)方法来判断obj在当前list的位置

 List<String> list3 = new ArrayList<>();list3.add("S");list3.add("C");System.out.println(list3.contains(new String("S"))); // trueSystem.out.println(list3.indexOf("S")); //0

这里contains我们传入了一个全新的String对象,但是结果还是为true,这是因为contains的实现是

public class ArrayList {Object[] elementData;public boolean contains(Object o) {for (int i = 0; i < elementData.length; i++) {if (o.equals(elementData[i])) {return true;}}return false;}
}

拿传入的对象,调用其equals方法,所以,要向正确的使用contains和indexOf,传入的对象就必须正确的实现equals方法。而String和Integer等对象,java已经帮我们实现了equals方法。

如何编写equals方法
  • 先确定实例“相等”的逻辑,即哪些字段相等,就认为实例相等;
  • 用instanceof判断传入的待比较的Object是不是当前类型,如果是,继续比较,否则,返回false;
  • 对引用类型用Objects.equals()比较,对基本类型直接用==比较。
class Person {private String name;private int age;public Person(String name, int age) {this.name = name;this.age = age;}public boolean equals(Object o) {if (o instanceof Person) {Person p = (Person) o;boolean nameEquals = false;if (this.name == null && p.name == null) {nameEquals = true;}if (this.name != null) {nameEquals = this.name.equals(p.name);}return nameEquals && this.age == p.age;}return false;}// 使用Objects.equals()比较两个引用类型是否相等的目的是省去了判断null的麻烦。两个引用类型都是null时它们也是相等的。public boolean equals1(Object o) {if (o instanceof Person) {Person p = (Person) o;return Objects.equals(this.name, p.name) && this.age == p.age;}return false;}
}
Map

有个需求,在存放Students 集合 List中查找一个名为小明的,查看他的成绩。
这时候我们需要遍历List,然后判断到name微小明的,就取他的成绩。这样效率很低。
而Map这种key value的形式刚好可以满足。

  Map<String, Person> map = new HashMap<>();map.put("lin", new Person("lin", 20));Person Target = map.get("lin");

和List类似,Map也是一个接口,最常用的实现类是HashMap。如果只是想查询某个key是否存在,可以调用boolean containsKey(K key)方法。
其他与js的Map类似。
遍历

  Person Target = map.get("lin");for (String key : map.keySet()) {System.out.println(key);}for (Map.Entry<String, Person> entry : map.entrySet()) {String key = entry.getKey();Person value = entry.getValue();System.out.println(key);System.out.println(value);}

map.keySet()获取key,map.entrySet()获取key-value

  • Map不保证顺序
编写equals 和 hashCode

Map之所以能很快通过key获取value,是因为内部通过空间换时间的处理。
Map内部用很大的数组存放数据,然后通过特殊的计算,比如f函数 f(key) = index,传入同一个key,index总是相同的,然后通过index存放value,取数据的时候也是通过f(key) = index计算出index,然后通过数组[index]去获取数据。但这样有个问题,如果传入的两个key是不同的对象呢。

 Map<String, String> map2 = new HashMap<>();map2.put("2", "123");System.out.println(map2.get(new String("2"))); // 123

传入不同的String还是能拿到正确的值,因为跟List一样,key值得比对也是通过equals方法。
其次,通过key获取index是通过hashCode方法获取的,

正确使用Map必须保证:
  • 作为key的对象必须正确覆写equals()方法,相等的两个key实例调用equals()必须返回true;
  • 作为key的对象还必须正确覆写hashCode()方法,且hashCode()方法要严格遵循以下规范:
  • 如果两个对象相等,则两个对象的hashCode()必须相等;
  • 如果两个对象不相等,则两个对象的hashCode()尽量不要相等
public class Person {String firstName;String lastName;int age;@Overrideint hashCode() {int h = 0;h = 31 * h + firstName.hashCode();h = 31 * h + lastName.hashCode();h = 31 * h + age;return h;}
}
// 借助Objects.hash
int hashCode() {return Objects.hash(firstName, lastName, age);
}

equals()用到的用于比较的每一个字段,都必须在hashCode()中用于计算;
equals()中没有使用到的字段,绝不可放在hashCode()中计算。

这里注意,两个对象的hashCode尽量不要想等,也就是有可能相等。

我们把不同的key具有相同的hashCode()的情况称之为哈希冲突。在冲突的时候,一种最简单的解决办法是用List存储hashCode()相同的key-value。显然,如果冲突的概率越大,这个List就越长,Map的get()方法效率就越低,这就是为什么要尽量满足条件二。
在这里插入图片描述

hashCode()方法编写得越好,HashMap工作的效率就越高。
计算hash code,相同hash code用一个List存放。

EnumMap

HsahMap通过空间换时间,计算index从数组直接拿数据。
如果key是Enum的话,可以用EnumMap ,在内部以一个非常紧凑的数组存储value,并且根据enum类型的key直接定位到内部数组的索引,并不需要计算hashCode(),不但效率最高,而且没有额外的空间浪费。

public class Main {public static void main(String[] args) {Map<DayOfWeek, String> map = new EnumMap<>(DayOfWeek.class);map.put(DayOfWeek.MONDAY, "星期一");map.put(DayOfWeek.TUESDAY, "星期二");map.put(DayOfWeek.WEDNESDAY, "星期三");map.put(DayOfWeek.THURSDAY, "星期四");map.put(DayOfWeek.FRIDAY, "星期五");map.put(DayOfWeek.SATURDAY, "星期六");map.put(DayOfWeek.SUNDAY, "星期日");System.out.println(map);System.out.println(map.get(DayOfWeek.MONDAY));}
}
TreeMap

HashMap无序,还有一种Map,它在内部会对Key进行排序,这种Map就是SortedMap。注意到SortedMap是接口,它的实现类是TreeMap。

       ┌───┐│Map│└───┘▲┌────┴─────┐│          │
┌───────┐ ┌─────────┐
│HashMap│ │SortedMap│
└───────┘ └─────────┘▲│┌─────────┐│ TreeMap │└─────────┘

SortedMap保证遍历时以Key的顺序来进行排序。例如,放入的Key是"apple"、“pear”、“orange”,遍历的顺序一定是"apple"、“orange”、“pear”,因为String默认按字母排序

 public static void main(String[] args) {Map<String, Integer> map = new TreeMap<>();map.put("orange", 1);map.put("apple", 2);map.put("pear", 3);for (String key : map.keySet()) {System.out.println(key);}// apple, orange, pear}

TreeMap要求实现Comparable方法
没有的话需要在使用TreeMap的时候指定排序方法,

public class Main {public static void main(String[] args) {Map<Person, Integer> map = new TreeMap<>(new Comparator<Person>() {public int compare(Person p1, Person p2) {return p1.name.compareTo(p2.name);}});map.put(new Person("Tom"), 1);map.put(new Person("Bob"), 2);map.put(new Person("Lily"), 3);for (Person key : map.keySet()) {System.out.println(key);}// {Person: Bob}, {Person: Lily}, {Person: Tom}System.out.println(map.get(new Person("Bob"))); // 2}
}

Comparator接口要求实现一个比较方法,它负责比较传入的两个元素a和b,如果a<b,则返回负数,通常是-1,如果a==b,则返回0,如果a>b,则返回正数,通常是1。TreeMap内部根据比较结果对Key进行排序

  • SortedMap在遍历时严格按照Key的顺序遍历,最常用的实现类是TreeMap;
  • 作为SortedMap的Key必须实现Comparable接口,或者传入Comparator;
  • 要严格按照compare()规范实现比较逻辑,否则,TreeMap将不能正常工作。
Set

不重复的集合,且不需要value,类似于js的Set

   Set<String> set = new HashSet<>();System.out.println(set.add("abc")); // trueSystem.out.println(set.add("xyz")); // trueSystem.out.println(set.add("xyz")); // false,添加失败,因为元素已存在System.out.println(set.contains("xyz")); // true,元素存在System.out.println(set.contains("XYZ")); // false,元素不存在System.out.println(set.remove("hello")); // false,删除失败,因为元素不存在System.out.println(set.size()); // 2,一共两个元素

set.add, set.contains, set.remove, set.resize
Set实际上相当于只存储key、不存储value的Map。我们经常用Set用于去除重复元素。
因为放入Set的元素和Map的key类似,都要正确实现equals()和hashCode()方法,否则该元素无法正确地放入Set

HashSet其实是封装HashMap

public class HashSet<E> implements Set<E> {// 持有一个HashMap:private HashMap<E, Object> map = new HashMap<>();// 放入HashMap的value:private static final Object PRESENT = new Object();public boolean add(E e) {return map.put(e, PRESENT) == null;}public boolean contains(Object o) {return map.containsKey(o);}public boolean remove(Object o) {return map.remove(o) == PRESENT;}
}

在这里插入图片描述

HasHSet是无序的,而SortedSet是有序的,但他只是个接口,实现的类是TreeSet

public class Main {public static void main(String[] args) {Set<String> set = new TreeSet<>();set.add("apple");set.add("banana");set.add("pear");set.add("orange");for (String s : set) {System.out.println(s);}}
}

使用TreeSet和使用TreeMap的要求一样,添加的元素必须正确实现Comparable接口,如果没有实现Comparable接口,那么创建TreeSet时必须传入一个Comparator对象。

Queue

队列,先进先出
它和List的区别在于,List可以在任意位置添加和删除元素,而Queue只有两个操作:

把元素添加到队列末尾;
从队列头部取出元素。
在这里插入图片描述
这些操作有两个方法,一个会抛出错误,一个返回false/null。

// 这是一个List:
List<String> list = new LinkedList<>();
// 这是一个Queue:
Queue<String> queue = new LinkedList<>();

LinkedList(链表)类实现了Queue接口和List接口

PriorityQueue

上述的Queue是先进先出,但是如果有优先级的处理,就像react中优先级高的渲染,就出现了优先级队列PriorityQueue,
PriorityQueue和Queue的区别在于,它的出队顺序与元素的优先级有关,对PriorityQueue调用remove()或poll()方法,返回的总是优先级最高的元素。(react-sceduler的实现类似)

public class Main {public static void main(String[] args) {Queue<String> q = new PriorityQueue<>();// 添加3个元素到队列:q.offer("apple");q.offer("pear");q.offer("banana");// 按照元素顺序System.out.println(q.poll()); // appleSystem.out.println(q.poll()); // bananaSystem.out.println(q.poll()); // pearSystem.out.println(q.poll()); // null,因为队列为空}
}

放入PriorityQueue的元素,必须实现Comparable接口,PriorityQueue会根据元素的排序顺序决定出队的优先级。
也可以在创建PriorityQeue的时候提供一个comparator对象来判断两个元素的顺序,跟TreeMap类似。

public class Main {public static void main(String[] args) {Queue<User> q = new PriorityQueue<>(new UserComparator());// 添加3个元素到队列:q.offer(new User("Bob", "A1"));q.offer(new User("Alice", "A2"));q.offer(new User("Boss", "V1"));System.out.println(q.poll()); // Boss/V1System.out.println(q.poll()); // Bob/A1System.out.println(q.poll()); // Alice/A2System.out.println(q.poll()); // null,因为队列为空}
}class UserComparator implements Comparator<User> {public int compare(User u1, User u2) {if (u1.number.charAt(0) == u2.number.charAt(0)) {// 如果两人的号都是A开头或者都是V开头,比较号的大小:return u1.number.compareTo(u2.number);}if (u1.number.charAt(0) == 'V') {// u1的号码是V开头,优先级高:return -1;} else {return 1;}}
}
Deque双端队列

,允许两头都进,两头都出,这种队列叫双端队列

Java集合提供了接口Deque来实现一个双端队列,它的功能是:

既可以添加到队尾,也可以添加到队首;
既可以从队首获取,又可以从队尾获取。

在这里插入图片描述
Deque是一个接口,它的实现类有ArrayDequeLinkedList

Stack 先进后出

在这里插入图片描述
将十进制转为16进制

public static void main(String[] args) {String hex = toHex(12500);System.out.println(hex);if (hex.equalsIgnoreCase("30D4")) {System.out.println("测试通过");} else {System.out.println("测试失败");}}static String toHex(int n) {Deque<String> dq = new LinkedList<>();int num = n % 16;int num1 = n / 16;while (num1 != 0) {dq.addFirst(Integer.toHexString(num));num = num1 % 16;num1 = num1 / 16;}dq.addFirst(Integer.toHexString(num));String result = "";String s = dq.pollFirst();System.out.println(s);while (s != null) {result += s;s = dq.pollFirst();}return result;}

循环求余数压入栈中,再循环取出。

Iterator

Java的集合类都可以使用for each循环,List、Set和Queue会迭代每个元素,Map会迭代每个key。因为他们都实现了Iterator对象
Java编译器并不知道如何遍历List。上述代码能够编译通过,只是因为编译器把for each循环通过Iterator改写为了普通的for循环:

for (Iterator<String> it = list.iterator(); it.hasNext(); ) {String s = it.next();System.out.println(s);
}
for (String s : list) {System.out.println(s);
}

类似于js的Symbol.Iterator迭代器,只要实现了Symbol.iterator,就可以用for of循环。
实现一个集合,需要实现他的Iterator方法,并且返回一个Iterator对象

class ReverseList<T> implements Iterable<T> {private List<T> list = new ArrayList<>();public void add(T t) {list.add(t);}@Overridepublic Iterator<T> iterator() {return new ReverseIterator(list.size());}class ReverseIterator implements Iterator<T> {int index;ReverseIterator(int index) {this.index = index;}@Overridepublic boolean hasNext() {return index > 0;}@Overridepublic T next() {index--;return ReverseList.this.list.get(index);}}
}

可以看到迭代器主要实现hasNext方法和next方法。js的Symbol.Iterator则是实现{done: ture, value: ‘’}来判断是否已经遍历结束。

Collections

Collections是JDK提供的工具类,同样位于java.util包中。它提供了一系列静态方法,能更方便地操作各种集合。

 // 空集合List<String> list2 = Collections.emptyList(); // 返回不可变List// 单元素集合List<String> list3 = Collections.singletonList("apple"); // 返回不可变ListList<String> list4 = Arrays.asList("1230", "456", "132"); // 返回不可变List// 排序Collections.sort(list4); // 排序回修改List元素的位置,所以需要传入可变ListSystem.out.println(list4);Collections.shuffle(list4); // 打乱List的顺序System.out.println(list4);// 变为不可变集合 假设List4是可变ListList<String> list5 = Collections.unmodifiableList(list4);// list5.add("orange"); // UnsupportedOperationException!list4.add("2222");list4 = null; // 转变后立马扔掉老的引用System.out.println(list5); // 老的List修改影响不可变List
小结
List
  • Java集合使用统一的Iterator遍历
  • 有序集合 List -> ArrayList(通过数组实现,) LinkedList(通过链表实现),可以用for each遍历。存放List的对象需要实现equals方法,便于调用List的contains和indexOf方法。
Map
  • key-value 映射表 -> Map , Map集合无序,可以通过for each遍历keySet(),也可以通过for each遍历entrySet(),直接获取key-value。最常用的Map就是HashMap

  • HashMap通过数组的形式存放,调用key.hashCode()获取索引,通过key.equals()判断是否相等,所以HashMap存放到对象需要实现hashCode和equals方法,而且equals方法返回true,则hashCode返回的索引一定相等,如果equals返回false,那么hashCode返回的值尽两不要相等。

    • 但是万一相等了呢,HashMap的数组存放的是List<Entry<string, Object>>,当不同的key的HashCode返回的索引相等的时候,会拿到一个List,再通过key.equlas去获取到对应的值。
  • 如果Map的key是enum类型,推荐使用EnumMap,既保证速度,也不浪费空间。使用EnumMap的时候,根据面向抽象编程的原则,应持有Map接口。

  • HashMap是无序的,但是Map下面还有一个SortedMap接口,TreeMap类实现了这个接口,他是有序的。

    • TreeMap怎么排序呢,通过存放的每个对象实现一个comparable接口,或者在定义TreeMap的时候,指定一个自定义排序算法
    •   Map<Person, Integer> map = new TreeMap<>(new Comparator<Person>() {public int compare(Person p1, Person p2) {return p1.name.compareTo(p2.name);}});
      
    • Comparator接口要求实现一个比较方法,它负责比较传入的两个元素a和b,如果a<b,则返回负数,通常是-1,如果a==b,则返回0,如果a>b,则返回正数,通常是1。TreeMap内部根据比较结果对Key进行排序。
Set
  • 只需要存储不重复的key,并不需要存储映射的value,那么就可以使用Set。
  • 最常用的Set是HashSet,他是HashMap的封装
  • 此外HashSet是无序的,但同时,SrotedSet是有序的,他是一个接口,TreeSet实现了这个接口
  • 跟TreeMap一样,TreeSet也要求一个compator接口,来判断两个对象的顺序。
Queue 通过LinkedList类实现该接口
  • 队列先进先出,通过add()/offer()方法将元素添加到队尾
  • 通过remove/poll获取队首元素并挪去。
  • 通过element()/peek()获取队首元素但不去掉。
  • 前面的add/remove/eleemtn执行出错会报错,而offer/poll/peek只返回对应的false/null
  • PriorityQueue优先级队列,优先级队列需要实现Comparable接口,priorityQueue根据元素的优先顺序决定谁先出队。
  • PriorityQueue默认按元素比较的顺序排序(必须实现Comparable接口),也可以通过Comparator自定义排序算法(元素就不必实现Comparable接口)。
Deque 双头队列 也是LinkedList实现
  • Deque其实也是继承于Queue的
  • addLast/offerLast addFirst/offerFirst 从队头/队尾插入
  • removeFirst/pollFirst removeLast/pollLast 从对头/队尾取出并删除
  • getFirst/peekFirst getLast/peekLast 从对头/队尾取出但不删除
Stack 栈,先进后厨
  • 利用Deque实现栈即可
  • 只调用对应的offerFirst/peekFirst/getFirst三个方法。
  • Stack作用很大,比如计算机调用方法通过执行栈维护等等
Iterator

Iterator是一种抽象的数据访问模型。使用Iterator模式进行迭代的好处有:

  • 对任何集合都采用同一种访问模型;
  • 调用者对集合内部结构一无所知;
  • 集合类返回的Iterator对象知道如何迭代。
  • Java提供了标准的迭代器模型,即集合类实现java.util.Iterable接口,返回java.util.Iterator实例。
Collections

Collections类提供了一组工具方法来方便使用集合类:

  • Collections.emptyList()创建空集合 返回不可变集合
  • Collections.singletonList(“test”)创建单个集合 返回不可变集合
  • Collections.unmodifiableList(可变集合),将可变集合变成不可变集合
  • Collections.sort()排序集合, Collections.shuffle() 将集合的顺序重新打乱(洗牌)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/712659.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

经典文献阅读之--CamMap(基于SLAM地图对不共视相机进行外参标定)

0. 简介 由于多相机之间通常存在有限或无重叠的视场&#xff0c;因此在估计外参相机参数时面临着一定的挑战&#xff0c;为了解决这个问题&#xff0c;本文提出了CamMap&#xff1a;一种新颖的6自由度外参标定流程。根据三个操作规则&#xff0c;使一个多相机系统单独捕捉一些…

【Linux进程】进程状态(运行阻塞挂起)

目录 前言 1. 进程状态 2. 运行状态 3. 阻塞状态 4. 挂起状态 5. Linux中具体的状态 总结 前言 在Linux操作系统中&#xff0c;进程状态非常重要&#xff0c;它可以帮助我们了解进程在系统中的运行情况&#xff0c;从而更好地管理和优化系统资源&#xff0c;在Linux系统中&am…

【Python笔记-设计模式】迭代器模式

一、说明 迭代器模式是一种行为设计模式&#xff0c;让你能在不暴露集合底层表现形式&#xff08;列表、栈和树等&#xff09;的情况下遍历集合中所有的元素。 (一) 解决问题 遍历聚合对象中的元素&#xff0c;而不需要暴露该对象的内部表示 (二) 使用场景 需要对聚合对象…

SpringBoot实现短链跳转

目录 1.背景介绍 2.短链跳转的意义 3.SpringBoot中的代码实现 1.建议短链-长链的数据库表&#xff1a;t_url_map: 2.映射实体 3.Dao层实现 4.Service层实现 5.Controller层实现 3.结果测试 4.问题 1.背景介绍 短链跳转是一种通过将长链接转换为短链接的方式&…

南方电网的能源棋局上,蔚来换电扮演什么角色?

2 月 26 日&#xff0c;南网储能科技与蔚来能源签署协议&#xff0c;将充换电站、储能站、可调负载等聚合资源连接到虚拟电厂平台&#xff0c;推动换电站作为分布式储能在虚拟电厂项目上的应用。 蔚来换电站是国内首个智慧微电网型分布式换电设施&#xff0c;可透过换电订单预…

软考-系统集成项目管理中级-信息系统建设与设计

本章重点考点 1.信息系统的生命周期 信息系统建设的内容主要包括设备采购、系统集成、软件开发和运维服务等。信息系统的生命周期可以分为四个阶段:立项、开发、运维和消亡。 2.信息系统开发方法 信息系统常用的开发方法有结构化方法、原型法、面向对象方法等 1)结构化方法 …

AI智能分析网关V4:抽烟/打电话/玩手机行为AI算法及场景应用

抽烟、打电话、玩手机是人们在日常生活中常见的行为&#xff0c;但这些行为在某些场合下可能会带来安全风险。因此&#xff0c;对于这些行为的检测技术及应用就变得尤为重要。今天来给大家介绍一下TSINGSEE青犀AI智能分析网关V4抽烟/打电话/玩手机检测算法及其应用场景。 将监控…

java项目打包运行报异常:xxxxx-1.0-SNAPSHOT.jar中没有主清单属性

pom.xml中加入这段话即可 <build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><version>2.4.4</version><executions><execution><…

安泰ATA-7050高压放大器在微流控细胞分选中的应用

微流控细胞分选是一种用于分离和鉴定生物样本中特定类型细胞的技术&#xff0c;其原理基于将生物细胞通过微通道进行操纵和区分。微流控细胞分选的原理主要基于流体力学、电气学、光学和热力学等多学科的交叉应用。通过设计具有特定尺寸和性质的微通道网络&#xff0c;可实现对…

RV1126芯片概述

RV1126芯片概述 前言1 主要特性2 详细参数 前言 1 主要特性 四核 ARM Cortex-A7 and RISC-V MCU250ms快速开机2.0Tops NPU14M ISP with 3帧 HDR支持3个摄像头同时输入4K H.264/H.265 视频编码和解码 2 详细参数

永磁同步电机无感FOC(龙伯格观测器)算法技术总结-仿真篇

文章目录 1、观测器的引入2、β轴向下的电机观测器数学模型3、β轴向下的转子点角度及速度观测4、Simulink仿真模型搭建4.1模型总览4.2 Luenberger观测器模块4.2.1 I_alpha观测4.2.2 I_beta观测4.2.3 e_alpha、e_beta观测4.2.4 锁相环 4.3 速度设定4.4 速度观测结果4.5 电角度观…

express+mysql+vue,从零搭建一个商城管理系统6--数据校验和登录

提示&#xff1a;学习express&#xff0c;搭建管理系统 文章目录 前言一、修改models/user.js二、修改routes下的user.js三、Api新建user/login接口四、删除数据库原有数据&#xff0c;添加新验证规则的用户四、用户登录总结 前言 需求&#xff1a;主要学习express&#xff0c;…

MacBook将iPad和iPhone备份到移动硬盘

#创作灵感# 一个是ICloud不够用&#xff0c;想备份到本地&#xff1b;然而本地存储不够用&#xff0c;增加容量巨贵&#xff0c;舍不得这个钱&#xff0c;所以就想着能不能备份到移动硬盘。刚好有个移动固态&#xff0c;所以就试了一下&#xff0c;还真可以。 #正文# 说一下逻…

《PyTorch深度学习实践》第八讲加载数据集

一、 1、DataSet 是抽象类&#xff0c;不能实例化对象&#xff0c;主要是用于构造我们的数据集 2、DataLoader 需要获取DataSet提供的索引[i]和len;用来帮助我们加载数据&#xff0c;比如说做shuffle(提高数据集的随机性)&#xff0c;batch_size,能拿出Mini-Batch进行训练。它…

Windows10环境下MongoDB安装配置

1. 下载对应MongoDB安装包 进入官网&#xff1a;MongoDB官网 如果不连接外网则在官网下载较慢&#xff0c;这里给出下载好的安装包&#xff0c;版本为4.2.25&#xff1a;百度网盘 选择你需要的版本&#xff0c;推荐选择Package的格式为zip&#xff08;解压即可&#xff09; Pa…

[VNCTF2024]-PWN:preinit解析(逆向花指令,绕过strcmp,函数修改,机器码)

查看保护&#xff1a; 查看ida&#xff1a; 这边其实看反汇编没啥大作用&#xff0c;需要自己动调。 但是前面的绕过strcmp还是要看一下的。 解题&#xff1a; 这里是用linux自带的产生随机数的文件urandom来产生一个随机密码&#xff0c;然后让我们输入密码&#xff0c;用st…

k8s 存储卷详解与动静部署详解

目录 一、Volume 卷 1.1 卷类型 emptyDir &#xff1a; hostPath&#xff1a; persistentVolumeClaim (PVC)&#xff1a; configMap 和 secret&#xff1a; 二、 emptyDir存储卷 2.1 特点 2.2 用途&#xff1a; 2.3 示例 三、 hostPath存储卷 3.1 特点 3.2 用途 …

前端mock数据 —— 使用Apifox mock页面所需数据

前端mock数据 —— 使用Apifox 一、使用教程二、本地请求Apifox所mock的接口 一、使用教程 在首页进行新建项目&#xff1a; 新建项目名称&#xff1a; 新建接口&#xff1a; 创建json&#xff1a; 请求方法&#xff1a; GET。URL&#xff1a; api/basis。响应类型&#xff1…

Socket网络编程(六)——简易聊天室案例

目录 聊天室数据传输设计客户端、服务器数据交互数据传输协议服务器、多客户端模型客户端如何发送消息到另外一个客户端2个以上设备如何交互数据&#xff1f; 聊天室消息接收实现代码结构client客户端重构server服务端重构自身描述信息的构建重构TCPServer.java基于synchronize…

Nginx多次代理后获取真实的用户IP访问地址

需求&#xff1a;记录用户操作记录&#xff0c;类似如下表格的这样 PS: 注意无论你的服务是Http访问还是Https 访问的都是可以的&#xff0c;我们服务之前是客户只给开放了一个端口&#xff0c;但是既要支持https又要支持http协议&#xff0c;nginx 是可以通过stream 模块配置双…