使用流将列表求和并限制总和

huangapple go评论79阅读模式
英文:

Sum up List to a limit with streams

问题

如何获取记录其计数总和应在限制范围内在下面的示例中存在`Records`对象包含recordId和count我想基于计数的总和应小于或等于我的限制条件来提取记录数据

```java
public class Records {
    private int recordID;
    private int count;

    public Records(int recordID, int count) {
        this.recordID = recordID;
        this.count = count;
    }

    public int getRecordID() {
        return recordID;
    }

    public void setRecordID(int recordID) {
        this.recordID = recordID;
    }

    public int getCount() {
        return count;
    }

    public void setCount(int count) {
        this.count = count;
    }
}


public static void main(String[] args) {
    
    final List<Records> recordList = new ArrayList<>();
    
    recordList.add(new Records(100, 10));
    recordList.add(new Records(501, 20));
    recordList.add(new Records(302, 5));
    recordList.add(new Records(405, 2));
    recordList.add(new Records(918, 8));
    int limit = 35;
}

预期结果

recordList应该包含记录对象:[100,10],[500,20],[302,5] records


<details>
<summary>英文:</summary>

How to get the records, which count sum should be in limit. In below example there is `Records` Object contains recordId and count, i wanted to fetch the records data based on the total sum of count should be less than or equal to my limit condition. 


```java
public class Records {
    private int recordID;
    private int count;

    public Records(int recordID, int count) {
        this.recordID = recordID;
        this.count = count;
    }

    public int getRecordID() {
        return recordID;
    }

    public void setRecordID(int recordID) {
        this.recordID = recordID;
    }

    public int getCount() {
        return count;
    }

    public void setCount(int count) {
        this.count = count;
    }
}


public static void main(String[] args) {
    
    final List&lt;Records&gt; recordList = new ArrayList&lt;&gt;();
    
    recordList.add(new Records(100, 10));
    recordList.add(new Records(501, 20));
    recordList.add(new Records(302, 5));
    recordList.add(new Records(405, 2));
    recordList.add(new Records(918, 8));
    int limit = 35;
}

Expected Result

recordList should have records objects : [100,10], [500,20], [302,5] records

答案1

得分: 1

使用Stream API解决这个问题的问题在于您必须在处理的上下文之外保留一些信息,并在同时进行读取/更新(依赖)它。这些任务不适合于Stream API。

相反,使用适合且非常适用于此的for循环:

int index = 0;                              // 最大可能的索引
int sum = 0;                                // 作为临时变量的总和
for (int i=0; i<recordList.size(); i++) {   // 对于每个记录
	sum += recordList.get(i).getCount();    // ... 将 'count' 添加到 'sum' 中
	if (sum <= limit) {                     // ... 直到总和低于限制
		index = i;                          // ... 移动中心点(pivot)
	} else break;                           // ... 否则停止处理
}

// 在这里,您需要获取从0到index+1的列表
// 因为subList(int, int)的第二个参数是排除的
List<Record> filteredRecords = recordList.subList(0, index + 1);
英文:

The problems of solving this with Stream API is that you have to keep some information outside of the context of processing and read/update (depend) on it at the same time. These tasks are not suitable for Stream API.

Use a for-loop instead which is suitable and great for this:

int index = 0;                              // highest index possible
int sum = 0;                                // sum as a temporary variable
for (int i=0; i&lt;recordList.size(); i++) {   // for each Record
	sum += recordList.get(i).getCount();    // ... add the &#39;count&#39; to the &#39;sum&#39;
	if (sum &lt;= limit) {                     // ... until the sum is below the limit
		index = i;                          // ... move the pivot
	} else break;                           // ... or else stop processing
}

// here you need to get the list from 0 to index+1 
// as long as the 2nd parameter of subList(int, int) is exlcusive
List&lt;Record&gt; filteredRecords = recordList.subList(0, index + 1);

答案2

得分: 0

这是我能想到的唯一方法,但它不像常规循环那样高效,因为它针对每个列表条目运行。这也导致它将其他值进一步添加到下面。例如,如果限制是46,则会跳过计数为5的第三个条目,但仍将添加计数为2的下一个条目。不知道这是否是您希望的行为。

AtomicInteger count = new AtomicInteger();

recordList = recordList.stream().filter(r -> {
    if(count.get() + r.count <= limit){
        count.addAndGet(r.count);
        return true;
    }
    return false;
}).collect(Collectors.toList());
英文:

This is the only thing I could come up with but it's not as efficient as a regular loop because it runs for each list entry it has. That also causes it to add other values further down. For example if limit was 46, third entry where count is 5 would be skipped but the next entry with count 2 would be still added. Don't know if this is desired behavior for you

    AtomicInteger count = new AtomicInteger();

    recordList = recordList.stream().filter(r -&gt; {
        if(count.get() + r.count &lt;= limit){
            count.addAndGet(r.count);
            return true;
        }
        return false;
    }).collect(Collectors.toList());

答案3

得分: 0

向您的类中添加以下的 `toString` 方法以便进行打印您可以按照以下方式进行操作

```java
public String toString() {
    return String.format("[%s, %s]", recordID, count);
}
  • 分配一个 List 以存储结果
  • 初始化总和变量
  • 遍历列表,将 count 累加,直到达到阈值。
List<Records> results = new ArrayList<>();
int sum = 0;
for (Records rec : recordList) {
    // 累加 counts
    sum += rec.getCount();
    if (sum > limit) {
        // 当超过限制时停止累加
        break;
    }
    results.add(rec);
}

results.forEach(System.out::println);

输出结果为

[100, 10]
[501, 20]
[302, 5]
英文:

Adding the following toString to your class for printing you can do it as follows:

public String toString() {
    return String.format(&quot;[%s, %s]&quot;, recordID, count);
}
  • Allocate a List to store the results
  • initialize the sum
  • iterate thru the list, summing the count until the
    threshhold is reached.
List&lt;Records&gt; results = new ArrayList&lt;&gt;();
int sum = 0;
for (Records rec : recordList) {
     // sum the counts
     sum += rec.getCount();
     if (sum &gt; limit) {
        // stop when limit exceeded
        break;
     }
     results.add(rec);
}
        
results.forEach(System.out::println);       

Prints

[100, 10]
[501, 20]
[302, 5]


</details>



# 答案4
**得分**: 0

使用Java 8,您可以这样做:

```java
public static void main(String[] args) {
    int limit = 35;
    List<Records> recordList = new ArrayList<>();
    recordList.add(new Records(100, 10));
    recordList.add(new Records(501, 20));
    recordList.add(new Records(302, 5));
    recordList.add(new Records(405, 2));
    recordList.add(new Records(918, 8));

    List<Records> limitedResult = recordList.stream().filter(new Predicate<Records>() {
        int sum = 0;
        @Override
        public boolean test(Records records) {
            sum = sum + records.getCount();
            return sum <= limit;
        }
    }).collect(Collectors.toList());
    // 对limitedResult进行操作
    System.out.println(limitedResult);
}

编辑:

或者您可以创建一个返回Predicate的函数,以便重用,例如:

// 可重用的Predicate
public static Predicate<Records> limitRecordPredicate(int limit){
    return new Predicate<Records>() {
        int sum = 0;
        @Override
        public boolean test (Records records){
            sum = sum + records.getCount();
            return sum <= limit;
        }
    };
}

然后像这样使用它:

List<Records> limitedResult = recordList.stream().filter(limitRecordPredicate(limit)).collect(Collectors.toList());
// 对limitedResult进行操作
System.out.println(limitedResult);

输出:

[Records[recordID=100, count=10], Records[recordID=501, count=20], Records[recordID=302, count=5]]
英文:

with java 8 you can do something like:

public static void main(String[] args) {
        int limit = 35;
        List&lt;Records&gt; recordList = new ArrayList&lt;&gt;();
        recordList.add(new Records(100, 10));
        recordList.add(new Records(501, 20));
        recordList.add(new Records(302, 5));
        recordList.add(new Records(405, 2));
        recordList.add(new Records(918, 8));

        List&lt;Records&gt; limitedResult = recordList.stream().filter(new Predicate&lt;Records&gt;() {
            int sum = 0;
            @Override
            public boolean test(Records records) {
                sum=sum+records.getCount();
                return sum &lt;= limit;
            }
        }).collect(Collectors.toList());
        //do what do you want with limitedResult
        System.out.println(limitedResult);
    }

Edit:

Or you can make function which return Predicate which can be reuse as:

    //Reusable predicate
    public static Predicate&lt;Records&gt; limitRecordPredicate(int limit){
        return new Predicate&lt;Records&gt;() {
            int sum = 0;
            @Override
            public boolean test (Records records){
                sum = sum + records.getCount();
                return sum &lt;= limit;
            }
        };
    }

and then used it like:

List&lt;Records&gt; limitedResult = recordList.stream().filter(limitRecordPredicate(limit)).collect(Collectors.toList());
        //do what do you want with limitedResult
        System.out.println(limitedResult);

Output:

[Records[recordID=100, count=10], Records[recordID=501, count=20], Records[recordID=302, count=5]]

huangapple
  • 本文由 发表于 2020年10月22日 03:25:56
  • 转载请务必保留本文链接:https://go.coder-hub.com/64470409.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定