如果Java集合有对称转换方法会怎样?
What If Java Had Symmetric Converter Methods on Collection?

原始链接: https://donraab.medium.com/what-if-java-had-symmetric-converter-methods-on-collection-cbb824885c3f

转换方法——将集合转换为不同类型的功能——对于可发现性和可用性至关重要,尤其是在一种语言提供多种集合选项时。Smalltalk 通过以 `as` 开头的方法为可变集合开创了这一模式。Java 的转换方法 (`to…`) 较为有限,通常需要多个步骤并模糊可变性。 Eclipse Collections 显著扩展了这一概念,在其 `RichIterable` 接口中提供了 26 个 `to…` 转换方法,在可变和不可变集合之间以及对象和原始类型之间提供对称性。这与 Java 形成对比,在 Java 中,确定可变性需要检查 Javadoc 或代码。 文章演示了在每种语言中将列表转换为排序列表、集合和包,突出了 Eclipse Collections 的易用性和清晰度。作者认为,设计良好的转换方法可以提高代码可读性和可维护性,从而降低长期成本。他们预计 Kotlin 等其他语言也会随着不可变集合的普及而出现类似的改进。最终,优先考虑清晰且一致的集合转换可以提升开发人员体验和代码质量。

黑客新闻 新 | 过去 | 评论 | 提问 | 展示 | 招聘 | 提交 登录 如果Java在集合上拥有对称转换方法会怎样? (donraab.medium.com) 16 分,由 xkriva11 1天前发布 | 隐藏 | 过去 | 收藏 | 讨论 指南 | 常见问题 | 列表 | API | 安全 | 法律 | 申请YC | 联系 搜索:
相关文章

原文

toBe(), or not toBe()?

Converter methods are more than a convenience in a programming language. They are a means to discovering additional collection types available to developers. When the number of available collection types is large, this becomes even more important to have good discoverability. Smalltalk has mostly mutable collection types. Java and Eclipse Collections both have mutable and immutable implementations. Only Eclipse Collections has mutable and immutable types as separate interfaces. Eclipse Collections also has primitive collections, so converter methods help provide helpful symmetry and discoverability between Object and primitive collection types.

toSmalltalk

In Smalltalk, converter methods are prefixed with as. The Collection abstract class has eleven converter methods — asArray, asBag, asByteArray, asCharacterSet, asDictionary, asIdentitySet, asMultilineString, asOrderedCollection, asOrderedDictionary, asSet, asSortedCollection.

This is the code from the above image inlined.

|ordered sorted set bag|
ordered := OrderedCollection with: 'Apple' with: 'Pear' with: 'Banana' with: 'Apple'.
sorted := ordered asSortedCollection: #yourself descending.
set := ordered asSet.
bag := sorted asBag.

Transcript show: ordered printString; cr.
Transcript show: sorted printString; cr.
Transcript show: set printString; cr.
Transcript show: bag printString; cr.

This is the output:

an OrderedCollection('Apple' 'Pear' 'Banana' 'Apple')
a SortedCollection('Pear' 'Banana' 'Apple' 'Apple')
a Set('Pear' 'Banana' 'Apple')
a Bag('Pear' 'Banana' 'Apple' 'Apple')

IIRC, most of the Collection types available via converter methods in Smalltalk are mutable.

toJava

In Java, converter methods are prefixed with to. The Collection interface has two converter methods — toString and toArray. The Stream interface has three converter methods —toString, toArray, toList. The Stream interface has a collect method which takes a Collector as a parameter. The Collectors utility class has nine unique to methods (some are overloaded) — toCollection, toList, toSet, toMap, toConcurrentMap, toUnmodifiableList, toUnmodifiableSet, toUnmodifiableMap, toConcurrentMap.

To convert a List to a “sorted” List, a Set, and a Bag , we can use the following. There is no SortedList or Bag type in Java, but we’ll find an equivalent.

@Test
public void converterMethodsInJava()
{
List<String> ordered =
List.of("Apple", "Pear", "Banana", "Apple");
List<String> sorted =
ordered.stream()
.sorted(Comparator.reverseOrder())
.toList();
Set<String> set =
ordered.stream()
.collect(Collectors.toSet());
Map<String, Long> bag =
sorted.stream()
.collect(Collectors.groupingBy(
Function.identity(),
Collectors.counting()));

assertEquals(
List.of("Pear", "Banana", "Apple", "Apple"),
sorted);
assertEquals(
Set.of("Pear", "Banana", "Apple"),
set);
assertEquals(
Map.of("Pear", 1L, "Banana", 1L, "Apple", 2L),
bag);
}

Most of the converter methods in Java are three steps away from Collection. I do not think it is likely we will see any more converter methods on Collection or Stream.

Note, in the code example above it is not easy to distinguish between mutable and immutable collection implementations. You have to read the Javadoc or code to understand the return types of different methods.

IntelliJ also recommends not using the converter method in the case of Collectors.toSet().

Press enter or click to view image in full size
stream().collect(Collectors.toSet()) shows up highlighted in yellow

IntelliJ recommends writing it as follows and this can be accomplished by hitting Alt-Enter and choosing the recommended action above.

Set<String> set = new HashSet<>(ordered);

Using this approach is more concise and is probably more performant (measure, don’t guess), but it introduces more asymmetry and draws implementation details (java.util.HashSet class) into our example.

toEclipseCollections

In Eclipse Collections, like Java, we use the to prefix for converter methods that have a linear time cost. The Eclipse Collections RichIterable interface has twenty six unique converter methods (some are overloaded). The converter methods can be found using IntelliJ Structure view in a category named “Converting”.

Press enter or click to view image in full size
Expanding the converter methods for Eclipse Collections in RichIterable in IntelliJ

To convert a List to a “sorted” List, a Set, and a Bag , we can use the following. There is no SortedList in Eclipse Collections, but we’ll find an equivalent. We will use mutable collections in these examples, and we can tell they are mutable based on the type names.

@Test
public void converterMethodsInEclipseCollections()
{
MutableList<String> ordered =
Lists.mutable.of("Apple", "Pear", "Banana", "Apple");
MutableList<String> sorted =
ordered.toSortedList(Comparator.reverseOrder());
MutableSet<String> set =
ordered.toSet();
MutableBag<String> bag =
sorted.toBag();

assertEquals(
List.of("Pear", "Banana", "Apple", "Apple"),
sorted);
assertEquals(
Set.of("Pear", "Banana", "Apple"),
set);
assertEquals(
Bags.mutable.withOccurrences("Apple", 2, "Pear", 1, "Banana", 1),
bag);
}

If we want to use immutable collections in Eclipse Collections, the code would look like this.

@Test
public void immutableConverterMethodsInEclipseCollections()
{
ImmutableList<String> ordered =
Lists.immutable.of("Apple", "Pear", "Banana", "Apple");
ImmutableList<String> sorted =
ordered.toImmutableSortedList(Comparator.reverseOrder());
ImmutableSet<String> set =
ordered.toImmutableSet();
ImmutableBag<String> bag =
sorted.toImmutableBag();

assertEquals(
List.of("Pear", "Banana", "Apple", "Apple"),
sorted);
assertEquals(
Set.of("Pear", "Banana", "Apple"),
set);
assertEquals(
Bags.mutable.withOccurrences("Apple", 2, "Pear", 1, "Banana", 1),
bag);
}

I did not have to change the assertions in the example. The equals and hashCode contract for mutable and immutable types of the same container type is the same.

Takeaways

Java is a great language. Java’s standard Collection library is useable but does not have great symmetry or convenience. Eclipse Collections brings back and extends the conveniences Smalltalk had thirty years ago into Java today, and adds symmetry for mutable and immutable converter methods.

Think about the code you are writing today, and what it will be like maintaining it for the next 5, 10, 20 or 30 years. Writing code that communicates well helpful in reducing the cost of understanding and maintenance. Well written code will also help new developers learn how things work, without having to memorize a lot of asymmetric alternatives to converting between collection types.

If you want to learn more about converter methods in Eclipse Collections, I have blogged about them previously, and they are also covered in Chapter 4 of the book “Eclipse Collections Categorically.” Here is a table of most of the mutable and immutable converter methods described in Chapter 4.

Press enter or click to view image in full size
Converting between RichIterable Types from Eclipse Collections Categorically.

and I also covered some converter methods in our “Refactoring to Eclipse Collections” talk at dev2next which I blogged about here.

If you don’t think this applies to you because you’ve moved to Kotlin, Python, Ruby, or some other language, take a look at the Kotlin Collections to methods. Once Immutable Collections become part of the Kotlin standard library, I will take an educated guess how this will grow.

Press enter or click to view image in full size
Kotlin Collections converter methods

Thanks for reading!

I am the creator of and committer for the Eclipse Collections OSS project, which is managed at the Eclipse Foundation. Eclipse Collections is open for contributions. I am the author of the book, Eclipse Collections Categorically: Level up your programming game.

联系我们 contact @ memedata.com