Java中的数据结构。 有用的助手类方法

哈ha!

我是EPAM的软件工程师。 八年来,我一直在使用用Java编写的遗留代码(预期注释,我注意到对遗留的理解和容忍早在EPAM之前就开始了,总之,您将找到原因的答案)。 在工作中,我经常遇到相同的重复缺陷。 这促使我写了一个笔记,我想从数据结构和助手类CollectionsArrays开始 。 由于某些原因,一些开发人员忽略了它们的使用,但徒劳无功

Java开发人员通常必须处理各种数据结构。 它可以是数组,各种Map的集合或实现。 似乎与它们有关的一切都是清晰易懂的,但是有些小事情很容易被发现。

对于不了解这些细微差别的初学者和可能忘记其中一些细节的有经验的开发人员而言,本笔记可能很有用。

图片
照片由ammiel jr在Unsplash上​​拍摄


我想马上保留一下该材料与Java 8相关的内容。很明显,在Java 9+中某些事情已经做得更好,但是大多数大型项目最经常使用Java 8(有时是Java 6)的版本。

获取基于数组的集合的最佳方法是什么?


我建议从基于数组的集合的形成开始。

通常,此方法发生:

Integer[] someArray = {9, 10, 11, 12}; List<Integer> list = Arrays.asList(someArray); 

当然可以,但是一切正常吗? 还有其他解决方案吗?

立即想到此方法的两个缺点:

  • 首先, Arrays.asList方法返回一个List 。 但是,如果我们需要Collection的另一个实现怎么办? Arrays.asList不允许这样做,但可以考虑其他方法。
  • 其次,通过调用Arrays.asList获得的List不支持调整大小。 我认为许多人都提出了处理此类清单的例外。

Collections接口上,可以找到Arrays.asList方法的替代方法-Collections.addAll方法。 使用方法如下:

 //      (List, Set, ...) Collection<Integer> collection = ...; Integer[] someArray = {9, 10, 8, 7}; Collections.addAll(collection, someArray); 

或者简单地:

 Collections.addAll(collection, 11, 12, 13, 14); 

Collections.addAll方法在输入处接受Collection对象和一个数组。 除了数组,还可以指定用逗号分隔的元素。

Arrays.asList相比Collections.addAll有什么好处?

  • 首先,在基于Collections.addAll数组创建集合时,它的工作速度比使用Arrays.asList输入的集合的addAll方法要快得多。 可以在此方法的JavaDoc中找到:
    此便捷方法的行为与c.addAll(Arrays.asList(elements))的行为相同,但是在大多数情况下,此方法的运行速度可能明显更快。
  • 此外, Collections.addAll不仅适用于List ,而且适用于任何其他收藏。
  • 使用此方法时,没有大小调整的问题。

打印数组,多维数组或集合的最简单方法是什么?


让我们继续讨论获取数组和集合的打印表示形式的问题。

如果我们仅制作System.out.println(someArray) ,我们将得到如下内容:
[Ljava.lang.Integer; @ 6d06d69c。
在数组上使用toString()方法时,预期会得到类似的结果。
要输出数组,请使用Arrays.toString(...)方法。

 Integer[] someArray = new Integer[]{1, 2, 3}; System.out.println(Arrays.toString(someArray)); 

该行的输出为:

 [1,2,3] 

如果我们在谈论多维数组,则可以使用方法: Arrays.deeptoString

 int[][] a = { {1, 2, 3}, {4, 5, 6} }; System.out.println(Arrays.deepToString(a)); 

该代码段的输出将是:

  [[1、2、3],[4、5、6]] 


因此,没有必要通过任何循环手动对数组进行排序以显示其元素;使用此方法就足够了。

至于Map的集合或实现,没有问题。 通常会输出除数组以外的所有数据结构。

假设有一个例子:

 Collection<Integer> someCollection = new HashSet<>(); someCollection.add(1); someCollection.add(2); System.out.println(someCollection); Map<Integer, String> someMap = new HashMap<>(); someMap.put(1, "Some 1"); someMap.put(2, "Some 2"); System.out.println(someMap); 

请注意,在下面的输出中,集合和地图均以易于阅读的格式显示:

 [1,2] 
 {1 =大约1,2 =大约2} 


相互比较数组有多容易?


在某些情况下,您需要比较数组。 Arrays类中有一个方法可以进行这种比较。 Arrays.equals方法比较元素数并检查相应元素的等效性。

假设我们有一个Element类,其中包含一个字段并具有一定的相等性

 private class Element { final String name; private Element(String name) { this.name = name; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Element element = (Element) o; return Objects.equals(name, element.name); } @Override public int hashCode() { return Objects.hash(name); } } 

定义三个数组:

 Element[] firstElementArray = { new Element("a"), new Element("b"), new Element("c") }; Element[] secondElementArray = {new Element("c"), new Element("b"), new Element("a") }; Element[] thirdElementArray = { new Element("a"), new Element("b"), new Element("c") }; 

请注意,第一和第三数组的元素顺序相同。
现在,您可以使用Arrays.equals方法执行比较。

 System.out.println("first equals to second? " + Arrays.equals(firstElementArray, secondElementArray)); System.out.println("second equals to third? " + Arrays.equals(secondElementArray, thirdElementArray)); System.out.println("first equals to third? " + Arrays.equals(firstElementArray, thirdElementArray)); 

结果如下:

第一等于第二? 错误的 
第二等于第三? 错误的 
第一等于第三? 是真的 


如何有效地复制数组?


通常,您可以在代码中看到使用循环手动复制数组。 但是,有一个System.arraycopy方法可以更快地复制。

我建议看一个简单的例子:

 Element[] elements = { new Element("a"), new Element("b"), new Element("c") }; Element[] copyOfElements = new Element[elements.length]; System.arraycopy(elements, 0, copyOfElements, 0, elements.length); System.out.println(Arrays.toString(copyOfElements)); 

我们有一系列元素。 我们创建一个长度相同的空数组,并将所有元素从第一个复制到第二个。 结果,我们得出以下结论:

 [元素{name ='a'},元素{name ='b'},元素{name ='c'}] 


如何以不同的方式对数组或集合进行排序?


可以使用Arrays.sort(someArray)方法对数组进行排序。 如果要以相反的顺序对数组进行排序,则可以将Collections.reverseOrder()作为第二个参数传递给此方法的输入。

例如,有一个数组,我们先按顺序然后按相反顺序排序:

 String[] someArray = new String[]{"b", "a", "c"}; Arrays.sort(someArray); System.out.println(Arrays.toString(someArray)); Arrays.sort(someArray, Collections.reverseOrder()); System.out.println(Arrays.toString(someArray)); 

结论如下:

 [a,b,c] 
 [c,b,a] 


除了直接和反向排序外,有时还需要不管大小写对字符串数组进行排序。 通过将String.CASE_INSENSITIVE_ORDER作为第二个参数传递给Arrays.sort,很容易做到这一点。

不幸的是, Collections.sort仅允许对List实现进行排序。

什么算法对Java进行排序?


在Java中谈论排序时要提到的最后一件事是在Java中,“简单排序”用于最简单的类型,而“稳定合并”用于对象。 因此,在探查器表明有必要之前,您不应该花费资源来开发自己的排序方法实现。

如果我们有一个数组并且该方法接受Iterable怎么办?


我现在建议继续讨论这样一个问题,即将数组传递给需要Iterable的方法 。 让我提醒您, Iterable是一个包含iterator()方法的接口,Iterator应该返回该方法。

如果有一个在输入处接受Iterable的方法,则无法像这样将数组转移到那里。 尽管您可以在for循环中遍历数组,但它不是Iterable

 String[] someArray = new String[]{"a", "b", "c"}; for (String currentString : someArray) { ... } 

在此示例中,一切都很好。 但是如果有一种方法:

 private static void someIteration(Iterable<String> iterable) { ... } 

该行将无法编译:

 someIteration(someArray); 

在这种情况下,唯一的解决方法是将数组转换为集合,并已将其提供给这种方法。

简要介绍一些有用的Collections方法


方法评注
最大(集合)最大(集合,比较器)
min(集合)min(集合,比较器)
请注意,您可以申请比较器的输入
indexOfSubList(列表,列表)
查找一个列表(第二个参数)在另一个列表(第一个参数)中第一次出现的索引
lastIndexOfSubList(列表,列表)
查找一个列表(第二个参数)在另一个列表(第一个参数)中最后一次出现的索引
反向(列表)
以相反的顺序重新排列项目

什么值得一读?


这只是使开发人员在使用数据结构时更轻松的工具的一小部分。 布鲁斯·埃克尔(Bruce Eckel)的书“ Java哲学”(第4版)中提供了馆藏本身的许多有趣之处以及使用它们的便捷工具。 但是,您应该小心,因为它遇到了无法在Java 7,Java 8和更高版本上播放的情况。 尽管本书对Java 6进行了描述,但是今天它的材料仍然很重要。

当然,“ Java哲学”不应受到限制。 读任何这些书都不会伤害任何Java开发人员:

  • “ Java。 《有效的编程》,约书亚·布洛赫(Joshua Bloch)。
  • “重构。 改进现有代码的设计,” Martin Fowler。
  • “清理代码。 创建,分析和重构”,Robert Martin。
  • Professional,Julian Kozmin等人的Spring 5。
  • “测试驱动的Java开发”,Viktor Farcic,Alex Garcia(尚未以俄语发布)。

结果如何?


如果您提出了可以补充本文所写内容的有趣想法,请在评论中分享它们。

我还要祝愿那些使用旧版旧代码的人好运和耐心。 大多数重大项目都是遗留项目。 而且它们对客户的重要性很难被高估。 花费了一个多星期的时间来找出原因,消除了该错误所带来的胜利感并没有逊色于新功能实施结束时的感觉。

谢谢您的关注。 如果提出的任何建议有用,我将感到高兴。
一切成功!

Source: https://habr.com/ru/post/zh-CN476098/


All Articles