public static void main(String[] args) throws IOException
{
HashSet set = new HashSet<String>();
set.add("{}");
set.add("{a}");
set.add("{b}");
set.add("{a, b}");
set.add("{a, c}");
sortedSet(set);
}
public static void sortedSet(HashSet set)
{
List<String> setList = new ArrayList<String>(set);
List<String> orderedByAlpha = new ArrayList<String>(set);
//sort by alphabetical order
orderedByAlpha = (List<String>) setList.stream()
.sorted((s1, s2) -> s1.compareToIgnoreCase(s2))
.collect(Collectors.toList());
System.out.println(orderedByAlpha);
}我尝试按字母顺序排序,但得到的输出如下:
[{a, b}, {a, c}, {a}, {b}, {}]但它应该是:
[{a}, {a, b}, {a, c}, {b}, {}]发布于 2018-11-19 07:13:15
您的输出与您的代码不匹配。您正在显示二维数组列表,但转换为一维数组列表没有任何意义。
public static void main(String[] args)
{
test(Arrays.asList("a", "d", "f", "a", "b"));
}
static void test(List<String> setList)
{
List<String> out = setList.stream().sorted((a, b) -> a.compareToIgnoreCase(b)).collect(Collectors.toList());
System.out.println(out);
}这是对一维数组的正确排序,所以你是对的。
您可能需要实现自己的比较器来比较二维数组列表,以便对它们进行排序。
发布于 2018-11-19 07:23:00
与其将源代码作为List<String>,我建议您将其作为List<Set<String>>。
List<Set<String>> setList = new ArrayList<>();
setList.add(new HashSet<>(Arrays.asList("a","b")));
setList.add(new HashSet<>(Arrays.asList("a","c")));
setList.add(new HashSet<>(Collections.singletonList("a")));
setList.add(new HashSet<>(Collections.singletonList("b")));
setList.add(new HashSet<>());然后将以下比较器与映射操作一起应用,以产生预期的结果:
List<String> result =
setList.stream()
.sorted(Comparator.comparing((Function<Set<String>, Boolean>) Set::isEmpty)
.thenComparing(s -> String.join("", s),
String.CASE_INSENSITIVE_ORDER))
.map(Object::toString)
.collect(Collectors.toList());打印结果如下:
[[a], [a, b], [a, c], [b], []]请注意,当前的结果是一个字符串列表,其中每个字符串都是给定集合的字符串表示。但是,如果您希望结果是一个List<Set<String>>,那么只需删除上面的map操作。
编辑:
根据你最初的想法,我设法得到了一个有效的解决方案……
因此,首先,您需要一个全新的比较器,而不仅仅是(s1, s2) -> s1.compareToIgnoreCase(s2),因为它还不够。
给定输入:
Set<String> set = new HashSet<>();
set.add("{}");
set.add("{a}");
set.add("{b}");
set.add("{a, b}");
set.add("{a, c}");和下面的流管道:
List<String> result = set.stream()
.map(s -> s.replaceAll("[^A-Za-z]+", ""))
.sorted(Comparator.comparing(String::isEmpty)
.thenComparing(String.CASE_INSENSITIVE_ORDER))
.map(s -> Arrays.stream(s.split(""))
.collect(Collectors.joining(", ", "{", "}")))
.collect(Collectors.toList());然后我们会得到一个结果:
[{a}, {a, b}, {a, c}, {b}, {}]发布于 2018-11-21 23:14:36
嗯,正如@Aomine和@Holger已经提到的,你需要一个自定义的比较器。
但是我觉得他们的解决方案看起来是过度设计的。您不需要任何代价高昂的操作,如split和substring
String.substring会创建一个新的String对象并在幕后调用System.arraycopy(),而String.split的开销会更大。它遍历您的字符串并多次调用String.substring。此外,它还创建了一个ArrayList来存储所有子字符串。如果子字符串的数量足够大,那么您的ArrayList将需要扩展其容量(可能不止一次),从而导致另一次System.arraycopy().调用
对于您的简单情况,我会稍微修改一下内置String.compareTo方法的代码:
Comparator<String> customComparator =
(s1, s2) -> {
int len1 = s1.length();
int len2 = s2.length();
if (len1 == 2) return 1;
if (len2 == 2) return -1;
int lim = Math.min(len1, len2) - 1;
for (int k = 1; k < lim; k++) {
char c1 = s1.charAt(k);
char c2 = s2.charAt(k);
if (c1 != c2) {
return c1 - c2;
}
}
return len1 - len2;
};它将比较复杂度为O(n)的字符串,其中n是较短字符串的长度。同时,它既不会创建任何新对象,也不会执行任何阵列复制。
可以使用Stream API实现相同的比较器
Comparator<String> customComparatorUsingStreams =
(s1, s2) -> {
if (s1.length() == 2) return 1;
if (s2.length() == 2) return -1;
return IntStream.range(1, Math.min(s1.length(), s2.length()) - 1)
.map(i -> s1.charAt(i) - s2.charAt(i))
.filter(i -> i != 0)
.findFirst()
.orElse(0);
};您可以像这样使用您的自定义比较器:
List<String> orderedByAlpha = setList.stream()
.sorted(customComparatorUsingStreams)
.collect(Collectors.toList());
System.out.println(orderedByAlpha);https://stackoverflow.com/questions/53366258
复制相似问题