Java自定义排序顺序Round-ish排序

时间:2018-09-15 21:45:16

标签: java sorting

我是Java开发人员,但是我正在针对一种我需要做的特定类型的好的算法作空。

基本上,我将从查询返回一些数据(最多几千行)。我只关心基于单个列的排序。具有讽刺意味的是,该列可能已经被排序了,但不是按照我需要的方式排序。

就是这样:

我正在获取一个用户ID列表,我需要对它们进行排序,以使其遍历整个列表并重新开始。一个简单的例子比解释容易:

让我们说数据是这样的:

A 一种 一种 一种 乙 乙 C d D

对我而言,有效的排序顺序为:

A 乙 C d 一种 乙 d 一种 A

基本上,我需要每个用户在转回他们之前都先“转弯”。用户数量可能会不均衡,因此任何多余的用户都可以在最后堆叠。

同样,我在Java中执行此操作,但此时仍未锁定到特定的数据结构中,等等。

[其他信息:如果有帮助,特别是我正在做的是为负载测试生成数据,并希望尽量减少同一用户多次登录应用程序,因此我希望测试循环遍历所有可用的应用程序用户然后再返回列表的开头。不过,这些数据是真实数据,我不能保证每个用户都会进行相同数量的活动。]

谢谢! 汤姆

3 个答案:

答案 0 :(得分:3)

这是我的解决方法。

不需要输入数据已经排序。

基本上,它将使用ID及其出现的次数创建一个Map,然后循环遍历此映射以每次选择一个不同的ID,直到映射为空。

public static void main(String[] args) {
    List<String> ids = Arrays.asList("A", "A", "A", "A", "B", "B", "C", "D", "D");
    List<String> idsOrdered = order(ids);
    idsOrdered.forEach(System.out::println);
}

private static List<String> order(List<String> ids) {
    // create a map with ids and their occurrences
    Map<String, Long> occurs = ids.stream().collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));

    List<String> idsOrdered = new ArrayList<>();
    while (!occurs.isEmpty()) {
        // add all the ids in the map to the list
        occurs.forEach((k, v) -> idsOrdered.add(k));
        // decrement the counter of all ids
        occurs.replaceAll((k, v) -> v - 1);
        // remove the ids with the counter at 0
        occurs.entrySet().removeIf(e -> e.getValue() == 0);
    }
    return idsOrdered;
}

这是相同的解决方案,但“老派”(无功能编程):

private static List<String> order(List<String> ids) {

    // create a map with ids and their occurrences
    Map<String, Integer> occurs = new HashMap<>();
    for (String id : ids) {
        Integer occur = occurs.get(id);
        if (occur != null) {
            occurs.put(id, occur + 1);
        }
        else {
            occurs.put(id, 1);
        }
    }

    List<String> idsOrdered = new ArrayList<>();
    while (!occurs.isEmpty()) {
        // loop through the map
        Iterator<Entry<String, Integer>> it = occurs.entrySet().iterator();
        while (it.hasNext()) {
            Map.Entry<String, Integer> pair = it.next();

            // add the key to the list
            String key = pair.getKey();
            idsOrdered.add(key);

            // update the occurrences, if 0 then remove the id from the map
            int newOccur = pair.getValue() - 1;
            if (newOccur == 0) {
                it.remove();
            }
            else {
                pair.setValue(newOccur);
            }
        }
    }
    return idsOrdered;
}

答案 1 :(得分:2)

  1. 通过任意分组功能将值分组为LinkedList类型结构。

  2. 通过轮询每个组将它们添加到最终结果集合中。

Ex)

给出:

public class MyObject {

    private String name;

    public MyObject(String name) {
        super();
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

数据集:

List<MyObject> objects = new ArrayList<>();


        objects.addAll( Arrays.asList(
            new MyObject("A"),
            new MyObject("C"),
            new MyObject("A"),
            new MyObject("B"),
            new MyObject("B"),
            new MyObject("B"),
            new MyObject("A"),
            new MyObject("A"),
            new MyObject("C"),
            new MyObject("C"),
            new MyObject("A"),
            new MyObject("C")
        ));

函数:

public static Queue<MyObject> robin(List<MyObject> objects) {

        if(objects.size() == 0) return new LinkedList<>();

        // Group into queues using the getName method
        Map<String, Queue<MyObject>> map = objects.stream()
                .collect(Collectors.groupingBy(MyObject::getName, Collectors.toCollection(LinkedList::new)));

        boolean remaining = true;
        Deque<MyObject> roundRobin = new LinkedList<MyObject>();

        Set<String> keySet = map.keySet();

        // Round robin structure to collect them into a single collection
        while(remaining) {
            remaining = false;
            for(String key : keySet) {
                MyObject obj = map.get(key).poll();
                if(obj == null) continue;
                roundRobin.add(obj);
                remaining = true;
            }
        }

        // Return result
        return roundRobin;
    }

结果排序:

A B C A B C A B C A C A

答案 2 :(得分:1)

基于

  

基本上,我需要每个用户先“转一下”,然后再回到   他们。用户数量可能会不均衡,因此任何额外的   只是堆在最后。

我不确定您的问题是否正在排序。在我看来,您想算一算(假设用户没有特定的正确顺序)。

如何将所有用户置于LinkedHashMap<User, Integer>中,从查询中插入所有用户,如果用户已经在地图中,则增加计数器。然后,您将循环浏览地图(用户现在始终处于相同的顺序),并在每个条目中调用您的工作,减少计数器,并在计数器达到零的情况下删除条目。 (一旦地图为空,则终止)

如果使用手动迭代器执行此操作,则计数器更新和条目删除将很快。

由于为每个用户创建了新对象,因此肯定会产生开销,因此这完全取决于每个用户的平均计数器值。